tag:blogger.com,1999:blog-69559162445275240202024-03-05T14:54:30.303+02:00ПостилкаIgorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.comBlogger78125tag:blogger.com,1999:blog-6955916244527524020.post-66553254842978564582015-01-13T11:22:00.000+02:002015-01-13T11:25:31.308+02:00Установка Gearman на Ubuntu<h4>Проблема:</h4>Старые версии Ubuntu не содеждат последние Gearman библиотеки. Если вы попробуете установить PHP расширение для Gearman на Ubuntu, то получите ошибку. Далее приведены шаги по установке Gearman библиотех и PHP расширения на Ubuntu 10.04 LTS и следующие версии.<br />
<br />
<h4>Добавляем PPA репозиторий для получения последней версии libgearman:</h4><pre class="brush: bash">$ sudo apt-add-repository ppa:gearman-developers
$ sudo apt-get update
$ sudo apt-get install libgearman7 libgearman-dev
</pre><br />
<h4>Устанавливаем Job Server:</h4><pre class="brush: bash">$ sudo apt-get install gearman-job-server
</pre><br />
<h4>Устанавливаем PHP расширение с помощью PECL:</h4><pre class="brush: bash">$ sudo pecl install gearman
</pre><br />
<h4>Активируем PHP расширение:</h4><pre class="brush: bash">$ echo "extension=gearman.so" | sudo tee /etc/php5/conf.d/gearman.ini
</pre><br />
<h4>Тестируем:</h4>Все готово для тестирования. Для этих целей можно использовать пример с официального сайта Gearman:<br />
<a href="http://gearman.org/examples/reverse/" target="_blank">http://gearman.org/examples/reverse/</a>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-72897222871943690282014-12-22T16:25:00.001+02:002014-12-22T16:25:19.278+02:00pkill: "Убийство" процессов по шаблонуПоследние пару недель много работаю с Gearman, поэтому приходится "убивать" много процессов.<br />
<br />
Например, у нас есть 3 запущенных php скрипта (в нашем случае 3 воркера)<br />
<pre class="brush: bash">$ ps aux | grep worker.php
isydorenko 8045 1.1 0.0 2578728 3784 s001 S 4:09PM 0:01.13 php worker.php
isydorenko 8037 1.0 0.1 2578728 5228 s001 S 4:09PM 0:01.13 php worker.php
isydorenko 8047 1.0 0.0 2578728 3772 s001 S 4:09PM 0:01.14 php worker.php
isydorenko 8089 0.0 0.0 2432784 628 s001 S+ 4:15PM 0:00.00 grep worker.php
</pre><br />
То есть мне нужно 3 раза запустить утилиту kill<br />
<pre class="brush: bash">$ kill -9 8045
$ kill -9 8037
$ kill -9 8047
</pre><br />
Я быстро устал "килять" каждый процесс и нашел утилиту pkill, которая "киляет" процессы по шаблону<br />
<pre class="brush: bash">$ pkill -9 -f worker.php
</pre>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-9234362032316154592014-12-19T00:58:00.004+02:002014-12-19T01:00:02.593+02:00tail: Отображение данных, находящихся в конце нескольких файловМы знаем о применении утилиты tail для отображения данных, находящихся в конце файла:<br />
<pre class="brush: bash">$ tail - f system.log
</pre><br />
Но ее можно использовать для нескольких файлов:<br />
<pre class="brush: bash">$ tail - f *.log
</pre><br />
В этом случае утилита показывает название файла и текст, который был туда записан.Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-53483561385721341222014-12-15T13:23:00.001+02:002014-12-15T13:24:32.114+02:00Magento: Как очистить DB Connections при форке процессаНа данный момент работаю над интеграцией <a href="http://gearman.org/" target="_blank">Gearman</a> в Magento. Для создания child-процессов использую <a href="http://php.net/manual/en/book.pcntl.php" target="_blank">PCNTL PHP extension</a>. Все шло гладко, но при тестировании я столкнулся с ошибкой "SQLSTATE[HY000]: General error: 2006 MySQL server has gone away". Проблема в том что при инициализации Magento приложения DB connections кешируются в Mage_Core_Model_Resource::$_connections и будут использоваться для всех форков родительского скрипта. Решение проблемы очень простое –– в дочернем скрипте необходимо удалить 'core/resource' из реестра:<br />
<br />
<pre class="brush: php">/**
* Reset DB connections for preventing "SQLSTATE[HY000]: General error: 2006 MySQL server has gone away"
*
* @return $this
*/
protected function _resetDbConnections()
{
Mage::unregister('_singleton/core/resource');
return $this;
}
</pre>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-55456389432369402792014-06-15T18:19:00.000+03:002014-06-16T11:06:46.609+03:00Magento: Как добавить во flat таблицу аттрибут с собственной source модельюДля добавления атрибута продукта во flat таблицу достаточно выполнить следующие действия:<br />
1. Включить для атрибута настройку "Used in Product Listing"<br />
2. Выполнить полный реиндекс catalog_product_flat<br />
<br />
Но это не работает если вы создаете атрибут с собственной source моделью. Чтобы сделать это нужно выполнить несколько дополнительных действий:<br />
1. Добавить в source модель функцию getFlatColums() с описанием вашего атрибута. Да да, именно getFlatColums, а не getFlatColum<strong>n</strong>s (привет core разработчикам Magento).<br />
<pre class="brush: php">/**
* {@inheritdoc}
*/
public function getFlatColums()
{
return array($this->getAttribute()->getAttributeCode() => array(
'type' => 'tinyint',
'unsigned' => true,
'is_null' => true,
'default' => null,
'extra' => null
));
}
</pre><br />
2. Добавить функцию getFlatUpdateSelect()<br />
<pre class="brush: php">/**
* {@inheritdoc}
*/
public function getFlatUpdateSelect($store)
{
/** @var $attr Mage_Eav_Model_Resource_Entity_Attribute */
$attr = Mage::getResourceSingleton('eav/entity_attribute');
return $attr->getFlatUpdateSelect($this->getAttribute(), $store);
}
</pre><br />
После этих действий не забываем выполнить полный реиндекс catalog_product_flat.Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-83370191018555269702014-04-06T13:28:00.000+03:002014-04-06T13:28:22.681+03:00Вышла beta-версия MAMP для WindowsЯ не раз упоминал что не люблю смешивать рабочее окружение и среду для программирования, стараюсь максимально разделить их – например, вынести на виртуалку. Но если выбирать среди сборок, то здесь лидирует <a href="http://www.mamp.info/en/">MAMP</a>. Программа очень мощная, гибкая, богатая на настройки. Например, она содержит несколько версий PHP (5.2.x, 5.3.x, 5.4.x, 5.5.x) и позволяет настроить отдельную версию для каждого хоста. То есть, один локальный сайт может работать с PHP 5.3.x, а другой c PHP 5.5.x. Согласитесь, очень удобно. Раньше это удовольствие было доступно только для Mac OS X, но пару дней назад <a href="http://blog-en.mamp.info/2014/04/mamp-for-windows-beta-released.html">вышла beta-версия для Windows</a>. Рекомендую посмотреть и попробовать.Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-88085990475308863282014-03-25T18:01:00.002+02:002014-03-25T18:01:52.462+02:00Узнать время выполнения скриптаЕсли вам нужно анализировать время выполнения скрипта (крон, индекс и т.д.), рекомендую посмотреть в сторону команды <strong>time</strong><br />
<pre class="brush: bash">$ time php -f indexer.php
</pre>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-40894343606103489422013-09-07T13:02:00.001+03:002013-09-07T17:36:21.517+03:00Установка расширения SHH2 для PHP на CentOSУстановить расширение SHH2 для PHP на CentOS очень просто. Его нельзя поставить через <i>yum install</i>, но все-равно установка сводится к выполнению нескольких простых шагов.<br />
<br />
Перед установкой необходимо проверить, что у вас установлен репозиторий EPEL. Если у вас отсутствует данный репозиторий, то перейдите на <a href="http://fedoraproject.org/wiki/About_EPEL">http://fedoraproject.org/wiki/About_EPEL</a> и следуйте инструкциям.<br />
<br />
После того как репозиторий EPEL установлен, можно приступать к установке завимостей.<br />
<pre class="brush: bash">yum install gcc php-devel php-pear libssh2 libssh2-devel
</pre><br />
Это позволит нам установить расширение SSH2 для PHP с помощью pecl.<br />
<pre class="brush: bash">pecl install -f ssh2
</pre><br />
Экстеншен установлен. Дайте опишем конфиг чтобы PHP мог использовать данное расширение.<br />
<pre class="brush: bash">touch /etc/php.d/ssh2.ini
echo extension=ssh2.so > /etc/php.d/ssh2.ini
</pre><br />
Если вы всё сделали правильно, то при выполнении следующей команды вы получите строку с "ssh2".<br />
<pre class="brush: bash">php -m | grep ssh2
</pre><br />
Расширение SSH2 готово к использованию. Не забудьте перезагрузить веб-сервер ;)Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-38568321186791305792013-09-06T17:24:00.002+03:002013-09-08T10:28:49.144+03:00Установка TeamCity на CentOSTeamCity — серверное программное обеспечение от компании JetBrains, написанное на языке Java, билд-сервер для обеспечения непрерывной интеграции.<br />
<br />
Я узнал о TeamCity два года назад, когда ребята поднимали CI на нашем проекте. В то время я не совсем понимал что из себя представляет CI и как это работает. Со временем мне пришлось создавать новые билды и описывать их конфигурацию в Ant-скриптах. Недавно мне пришлось самому устанавливать TeamCity на "чистую" CentOS. Об этом и расскажу. Будете удивлены, но делается это очень просто.<br />
<span class="fullpost"><br />
Для примера я взял CentOS 6.4 и TeamCity 8.3.<br />
<br />
1. Устанавливаем Oracle 1.6 JDK.<br />
<pre class="brush: bash">yum install java-openjdk
</pre><br />
2. Устанавливам tomcat 6.*-7.*. Я выбрал шестую версию. Разработчики TeamCity <a href="http://confluence.jetbrains.com/display/TCD8/Supported+Platforms+and+Environments">рекомендуют</a> Tomcat 6.0.27+.<br />
<pre class="brush: bash">yum install tomcat6 tomcat6-webapps tomcat6-admin-webapps
</pre><br />
4. Открываем <a href="http://www.jetbrains.com/teamcity/download/index.html">http://www.jetbrains.com/teamcity/download/index.html</a> и качаем версию для Linux.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrw_L8Rtj3vXFWSo9HZk4LmxHYCPelpKHUkYWrt5mfFhKd4-1Wzlcj6wl9Q8mEMs-wr0ICvCY12hsUfKZ-Wqg4ClvY49emqpxhKLJaSs7fGTlSOntoCmltPbnNEhX5iJw00OEMJKdxojWp/s1600/2013-09-05_2017.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrw_L8Rtj3vXFWSo9HZk4LmxHYCPelpKHUkYWrt5mfFhKd4-1Wzlcj6wl9Q8mEMs-wr0ICvCY12hsUfKZ-Wqg4ClvY49emqpxhKLJaSs7fGTlSOntoCmltPbnNEhX5iJw00OEMJKdxojWp/s1600/2013-09-05_2017.png" /></a><br />
<br />
<pre class="brush: bash">mkdir /opt/jetbrains
cd /opt/jetbrains
wget http://download.jetbrains.com/teamcity/TeamCity-8.0.3.tar.gz
tar -xzf TeamCity-8.0.3.tar.gz
</pre><br />
5. TeamCity скачан, распакован, и находится в папке <i>/opt/jetbrains/TeamCity</i>. Пришло время запустить TeamCity сервер и дефолтный билд-агент.<br />
<pre class="brush: bash">cd /opt/jetbrains/TeamCity/bin
sh runAll.sh start
</pre><br />
Всё готово. Теперь вы можете открыть http://your_host:8111/ и начинать работать с TeamCity. Если всё установилось правильно, то вы должны увидеть страницу с таким содержанием:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDR5nTK2tuTffGARLwoJP_frB80ujd873bY3MlvtDT9dVMXNo166CbebS1RPBG44603rjWGtW_y0Xg3m2zbxH1A1BSglc3u-pwWuOVFiH_O7Oed7biAvDTrJAX4BLB_yJzwwFIN1jdAFDq/s1600/2013-09-06_1554.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDR5nTK2tuTffGARLwoJP_frB80ujd873bY3MlvtDT9dVMXNo166CbebS1RPBG44603rjWGtW_y0Xg3m2zbxH1A1BSglc3u-pwWuOVFiH_O7Oed7biAvDTrJAX4BLB_yJzwwFIN1jdAFDq/s1600/2013-09-06_1554.png" /></a><br />
<br />
Я описал как просто и быстро "завести" TeamCity. О детальной настройке можно прочитать в <a href="http://www.jetbrains.com/teamcity/documentation/index.jsp">разделе документации</a> TeamCity.<br />
</span>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-60392490234508653802013-03-16T15:07:00.001+02:002013-03-16T23:31:19.708+02:00SVN: Как мерджить из одной ветки в другуюSubversion была моей основной CVS более трёх лет. С Git я познакомился всего год назад. Не скажу что мой мир сильно изменился, но он мне показался более удобным. По-этому свои личные проекты я храню только в Git, а две недели назад даже купил Micro Plan на GitHub. <br />
<br />
На работе же всё зависит от проекта: некоторые используют Git, остальные постепенно мигрируют с SVN на Git. Я сейчас работаю в команде, которая относится ко второму типу. По-этому я хочу рассказать о своем опыте мерджа из одной ветки в другую.<br />
<span class="fullpost"><br />
Нам понадобится <a href="http://www.orcaware.com/svn/wiki/Svnmerge.py" target="_blank">Svnmerge.py</a> и две ветки (например, http://svn.local/dev и http://svn.local/qa). Вторая ветка это копия первой, она была создана с помощью 'svn cp' и закомичена ревизией 12830.<br />
<br />
Теперь предположим что у нас есть задача, реализацию которой мы запилили в dev-ветку. Список ревизий: 12834, 12839, 12845. Теперь нами стоит задача мерджа этих ревизий в qa-ветку.<br />
<br />
1. Создаем папку и выполняем туда чекаут qa-ветки:<br />
<pre class="brush: bash">mkdir qa
svn co http://svn.local/qa qa
</pre><br />
2. После окончания чекаута нужно положить svnmerge.py в папку qa и сделать его исполняемым.<br />
3. Заходим в папку qa и инициализируем dev-ветку. Эту единоразовая операция, ее не придется повторять в будущем:<br />
<pre class="brush: bash">svnmerge.py init -r1-12830 http://svn.local/dev
svn ci -F svnmerge-commit-message.txt
</pre><br />
12830 это ревизия, в которой была создана qa-ветка. Мы записали в свойства бранча информацию о том, что в нем находится код dev-ветки с 1й по 12830й ревизии. Теперь у нас не получится повторно вмерджить эти ревизии. <br />
<br />
4. Собираем необходимые ревизии и проверяем их наличие в dev/qa ветках:<br />
<pre class="brush: bash">svnmerge.py avail -r12834,12839,12845 -Shttp://svn.local/dev
</pre><br />
Если это ревизии dev-верки и они не были ранее вмерджены в qa-ветку, то мы получим список доступных ревизий:<br />
<pre class="brush: bash">12834,12839,12845
</pre><br />
Если какая-то из ревизий отсутствует в списке, на это может быть 2 причины:<br />
<ul><li>Это не ревизия dev-ветки;</li>
<li>Это ревизия dev-ветки, но она уже была вмерджена в qa-ветку.</li>
</ul><br />
5. Мы предполагаем что у нас все хорошо и выполняем команду для мерджа:<br />
<pre class="brush: bash">svnmerge.py merge -r12834,12839,12845 -Shttp://svn.local/dev # мердж
svn st # проверяем изменения
svn ci -F svnmerge-commit-message.txt # комитим результат
</pre><br />
Как видите все не так просто как в Git, но и сложного ничего нет :) <br />
</span>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-37663290577042243842013-03-03T21:45:00.002+02:002013-03-03T21:50:31.029+02:00DLE: rewrite правила для nginxИсторически так сложилось, что DataLife Engine (DLE) это первая CMS, которую я изучил. По-этому некоторые проекты еще крутятся на ней. С каждый годом этот геморрой становится все больнее, но пока что нет времени все переписывать. <br />
<br />
2 недели назад мне пришло уведомление из <a href="http://itl.ua" target="_blank">ITL</a>, что на серваке, на котором лежит мой VPS, посыпался винт. Извинились, дали 99% скидку на 2 месяца и сказали что надо бы переезжать на новый VPS. Заказал, предоставили чистую FreeBSD и начал Игорь сисадминить.<br />
<span class="fullpost"><br />
Сразу решил полностью отказаться от Apache, использовать только nginx. Думаю, все те кто юзал DLE помнят .htaccess с большим количеством реврайтов. Это и была основная проблема при миграции с Apache+nginx на standalone nginx. Авторы DLE позаботились об этом и <a href="http://dle-news.ru/tips/770-pravila-rewrite-dlya-podderzhki-chpu-na-serverax.html" target="_blank">предоставили</a> rewrite правила для nginx (ссылка для тех, у кого <s>спизженная CMS</s> отсутствует лицензия - <a href="http://dlepro.com/939-pravila-rewrite-dlya-podderzhki-chpu-dle-na-serverah-pod-upravleniem-nginx.html" target="_blank">ТЫЦ</a>). Но лично мне они не понравились, по-этому я продолжил поиски и собрал из нескольких источников следующий конфиг:<br />
<br />
<pre class="brush: bash">server {
listen 80;
server_name YOUR_HOST;
root /path/to/document_root;
rewrite ^/page/(.*)$ /index.php?cstart=$1 last;
location / {
rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})(/?)+$" /index.php?year=$1&month=$2&day=$3 last;
rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/page/([0-9]+)(/?)+$" /index.php?year=$1&month=$2&day=$3&cstart=$4 last;
rewrite "^/([0-9]{4})/([0-9]{2})(/?)+$" /index.php?year=$1&month=$2 last;
rewrite "^/([0-9]{4})/([0-9]{2})/page/([0-9]+)(/?)+$" /index.php?year=$1&month=$2&cstart=$3 last;
rewrite "^/([0-9]{4})(/?)+$" /index.php?year=$1 last;
rewrite "^/([0-9]{4})/page/([0-9]+)(/?)+$" /index.php?year=$1&cstart=$2 last;
rewrite "^/([^.]+)/page/([0-9]+)(/?)+$" /index.php?do=cat&category=$1&cstart=$2 last;
rewrite "^/([^.]+)(/?)+$" /index.php?do=cat&category=$1 last;
index index.php index.html index.htm;
}
location /tags/ {
rewrite ^/tags/([^/]*)(/?)+$ /index.php?do=tags&tag=$1 last;
rewrite ^/tags/([^/]*)/page/([0-9]+)(/?)+$ /index.php?do=tags&tag=$1&cstart=$2 last;
}
location /user/ {
rewrite ^/user/([^/]*)/rss.xml$ /engine/rss.php?subaction=allnews&user=$1 last;
rewrite ^/user/([^/]*)(/?)+$ /index.php?subaction=userinfo&user=$1 last;
rewrite ^/user/([^/]*)/page/([0-9]+)(/?)+$ /index.php?subaction=userinfo&user=$1&cstart=$2 last;
rewrite ^/user/([^/]*)/news(/?)+$ /index.php?subaction=allnews&user=$1 last;
rewrite ^/user/([^/]*)/news/page/([0-9]+)(/?)+$ /index.php?subaction=allnews&user=$1&cstart=$2 last;
rewrite ^/user/([^/]*)/news/rss.xml(/?)+$ /engine/rss.php?subaction=allnews&user=$1 last;
}
location /lastnews/ {
rewrite ^/lastnews/(/?)+$ index.php?do=lastnews last;
rewrite ^/lastnews/page/([0-9]+)(/?)+$ /index.php?do=lastnews&cstart=$1 last;
}
location /catalog/ {
rewrite ^/catalog/([^/]*)/rss.xml$ /engine/rss.php?catalog=$1 last;
rewrite ^/catalog/([^/]*)(/?)+$ /index.php?catalog=$1 last;
rewrite ^/catalog/([^/]*)/page/([0-9]+)(/?)+$ /index.php?catalog=$1&cstart=$2 last;
}
location /newposts {
rewrite ^/newposts(/?)+$ /index.php?subaction=newposts last;
rewrite ^/newposts/page/([0-9]+)(/?)+$ /index.php?subaction=newposts&cstart=$1 last;
}
location /favorites {
rewrite ^/favorites(/?)+$ /index.php?do=favorites last;
rewrite ^/favorites/page/([0-9]+)(/?)+$ /index.php?do=favorites&cstart=$1 last;
}
location ~ \.(html|xml) {
rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/page,([0-9]+),([0-9]+),(.*).html(/?)+$" /index.php?subaction=showfull&year=$1&month=$2&day=$3&news_page=$4&cstart=$5&news_name=$6 last;
rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/page,([0-9]+),(.*).html(/?)+$" /index.php?subaction=showfull&year=$1&month=$2&day=$3&news_page=$4&news_name=$5 last;
rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/print:page,([0-9]+),(.*).html(/?)+$" /engine/print.php?subaction=showfull&year=$1&month=$2&day=$3&news_page=$4&news_name=$5 last;
rewrite "^/([0-9]{4})/([0-9]{2})/([0-9]{2})/(.*).html(/?)+$" /index.php?subaction=showfull&year=$1&month=$2&day=$3&news_name=$4 last;
rewrite "^/([^.]+)/page,([0-9]+),([0-9]+),([0-9]+)-(.*).html(/?)+$" /index.php?newsid=$4&news_page=$2&cstart=$3 last;
rewrite "^/([^.]+)/page,([0-9]+),([0-9]+)-(.*).html(/?)+$" /index.php?newsid=$3&news_page=$2 last;
rewrite "^/([^.]+)/print:page,([0-9]+),([0-9]+)-(.*).html(/?)+$" /engine/print.php?news_page=$2&newsid=$3 last;
rewrite "^/([^.]+)/([0-9]+)-(.*).html(/?)+$" /index.php?newsid=$2 last;
rewrite "^/page,([0-9]+),([0-9]+),([0-9]+)-(.*).html(/?)+$" /index.php?newsid=$3&news_page=$1&cstart=$2 last;
rewrite "^/page,([0-9]+),([0-9]+)-(.*).html(/?)+$" /index.php?newsid=$2&news_page=$1 last;
rewrite "^/print:page,([0-9]+),([0-9]+)-(.*).html(/?)+$" /engine/print.php?news_page=$1&newsid=$2 last;
rewrite "^/([0-9]+)-(.*).html(/?)+$" /index.php?newsid=$1 last;
rewrite "^/static/(.*).html(/?)+$" /index.php?do=static&page=$1 last;
rewrite ^/rules.html$ /index.php?do=rules last;
rewrite ^/statistics.html$ /index.php?do=stats last;
rewrite ^/addnews.html$ /index.php?do=addnews last;
rewrite ^/([^.]+)/rss.xml$ /engine/rss.php?do=cat&category=$1 last;
rewrite ^/page,([0-9]+),([^/]+).html$ /index.php?do=static&page=$2&news_page=$1 last;
rewrite ^/print:([^/]+).html$ /engine/print.php?do=static&page=$1 last;
rewrite ^/rss.xml$ /engine/rss.php last;
rewrite ^/sitemap.xml$ /uploads/sitemap.xml last;
rewrite ^/([^/]+).html$ /index.php?do=static&page=$1 last;
}
location ~* (uploads|uploads/fotos|templates|language)/.+\.php {
deny all;
}
location ~* /templates/.+\.tpl {
deny all;
}
location ~* (engine/cache) {
deny all;
}
location ~ /\.ht {
deny all;
}
location ~* \.(jpg|jpeg|gif|png|ico|swf|css|js)$ {
expires 30d;
add_header Cache-Control public;
}
# extra login to admin panel
location =/admin.php {
auth_basic "closed section";
auth_basic_user_file htpasswd;
fastcgi_pass unix:/tmp/fastcgi.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000; # or socket
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
</pre><br />
Конфиг проверен и успешно работает на моем проекте, но не стоит вслепую его копировать. Лучше разобраться: возможно, что-то вам покажется лишним или, наоборот, недостаточным.<br />
</span>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-46878780144221210722013-02-28T22:02:00.000+02:002013-03-03T11:56:47.003+02:00SVN: делаем файл исполняемымПоследние 4 дня занимаюсь созданием CI билдов для TeamCity. Одна из задач - запускать php скрипты через Apache Ant. Для того чтобы его запускать нужно сделать его исполняемым. Вариант добавления 'chmod +x file' в Ant скрипт не подходит, так как это костыль. <br />
<br />
Начал гуглить и <a href="http://svn.haxx.se/users/archive-2009-02/0801.shtml">нашел</a>.<br />
<br />
Чтобы сделать файл исполняемым необходимо выполнить:<br />
<pre class="brush: plain">svn propset svn:executable ON filename
</pre><br />
Обратная операция:<br />
<pre class="brush: plain">svn propdel svn:executable ON filename
</pre>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-52337814078482059062013-02-06T23:51:00.002+02:002013-02-07T08:07:59.744+02:00JavaScript: сортировка массивов и json-объектовСортировка массива:<br />
<pre class="brush: javascript">[1, 3, 9, 2].sort();
</pre><br />
С сортировкой json-объектов дела состоят немного сложнее, так как сортировать приходится по какому-то значению: цена, возраст и т.д. Для этого в функцию sort можно передавать функцию сортировки. <br />
<br />
Пример сортировки по-возрастанию:<br />
<pre class="brush: javascript">[
{ name: "Item 1", price: 2000 },
{ name: "Item 2", price: 1000 },
{ name: "Item 4", price: 1200 },
{ name: "Item 3", price: 3000 }
].sort(function(obj1, obj2) {
return obj1.price-obj2.price;
});
</pre><br />
Пример сортировки по-убыванию:<br />
<pre class="brush: javascript">[
{ name: "Item 1", price: 2000 },
{ name: "Item 2", price: 1000 },
{ name: "Item 4", price: 1200 },
{ name: "Item 3", price: 3000 }
].sort(function(obj1, obj2) {
return obj2.price-obj1.price;
});
</pre>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-8771463593404012722013-02-05T23:29:00.001+02:002013-02-06T10:38:01.732+02:00PHP: особенности функции ip2longДумаю, большинство программистов встречались с задачей хранения ip в БД. Хранить их как plain text (например, 127.0.0.1) неудобно и непрактично. Потому принято использовать функцию <a href="http://php.net/ip2long">ip2long</a>, которая преобразовывает ip адрес в целове число. <br />
<br />
Но есть некоторые особенности ее использования в x32/x64. Допустим, для его хранения мы выделяем INT ячейку в БД.<br />
<br />
<b>Результат функции на x32:</b><br />
ip2long(127.127.127.127) = 2147483648<br />
ip2long(255.255.255.255) = -2147483648<br />
<br />
<b>Результат функции на x64:</b><br />
ip2long(127.127.127.127) = 2147483648<br />
ip2long(255.255.255.255) = 4294967296<br />
<br />
Получается что 4294967296 не запишется в БД, так как ячейка в нашей БД типа INT и максимальное число, которое можно в нее записать: 2147483648.<br />
<br />
Потому добавляем для атрибут UNSIGNED, который позволяет записывать в БД диапазон чисел от 0 до 4294967296.<br />
<br />
Но как же быть с x32? Ведь в ней функция ip2long может вернуть отрицательное значение. Для решения этой проблемы нужно обрабатывать результат перед записью БД:<br />
<pre class="brush: php">$ip = sprintf('%u', ip2long($ip)); // возвращает строку с положительным значением
</pre>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com3tag:blogger.com,1999:blog-6955916244527524020.post-57678089462053081722013-02-02T11:31:00.004+02:002013-02-02T11:32:21.347+02:00Magento: модуль Customer Universal PasswordВчера заапрувили мое первое расширение для Magento. <br />
<br />
Magento Connect: <a href="http://www.magentocommerce.com/magento-connect/catalog/product/view/id/15763/">http://www.magentocommerce.com/magento-connect/catalog/product/view/id/15763/</a><br />
GitHub: <a href="https://github.com/sidorenko/Navoq_CustomerUniversalPassword">https://github.com/sidorenko/Navoq_CustomerUniversalPassword</a>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-37879311663920267682013-01-31T01:58:00.000+02:002013-01-31T01:58:03.276+02:00Magento: выключение всех модулей в local code poolMagento позволяет быстро и безболезненно выключить все модули, котолые лежат в local code pool (app/code/local). Для этого нужно открыть файл app/etc/local.xml и установить true для ноды disable_local_modules:<br />
<pre class="brush: xml"><config>
<global>
...
<disable_local_modules>true</disable_local_modules>
...
</global>
...
</config>
</pre><br />
Если у вас включено кеширование, то после сохранения изменений нужно очистить кеш. <br />
<br />
Это действие предотвратит использование любого кода из вашего local code pool и поможет разобраться с проблемами переопределения кода. Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-3401086273803985062013-01-20T13:56:00.001+02:002013-01-20T14:07:09.693+02:00PHP: кавычки в str_replace() Недавно передо мной стала задача удаления некоторых ASCII Device Control Characters из текста. Поскольку удалять нужно было определенные символы, я решил использовать <a href="http://php.net/str_replace">str_replace()</a>. В процессе имплементации было обнаружено разное поведение функции, в зависимости от типа кавычек, в которых мы передаем первый параметр.<br />
<br />
<b>Одинарные кавычки</b><br />
<pre class="brush: php">str_replace('\x07', '', $text);
</pre>В этом случае функция ищет вхождения плейнтекста '\x07'. <br />
<br />
<b>Двойные кавычки</b><br />
<pre class="brush: php">str_replace("\x07", '', $text);
</pre>В этом случае функция ищет вхождения ASCII символа '\x07'. Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com2tag:blogger.com,1999:blog-6955916244527524020.post-23982904643098931832012-12-08T17:07:00.000+02:002012-12-08T18:39:17.370+02:00Очистка memcacheОчистить memcache можно двумя способами: варварским и нормальным. "Варварский" это перезапуск сервиса memcached. "Человеческий" это присоединиться по telnet к memcache и выполнить команду flush_all.<br />
<br />
Например,<br />
<pre class="brush: bash">telnet 127.0.0.1 11211 // присоединяемся
flush_all // чистим
quit // разрываем соединение
</pre>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-64448751117111463472012-11-10T14:39:00.002+02:002012-11-10T14:55:06.659+02:00Magento: обновление отдельного атрибута продуктаПри работе с Magento часто возникают ситуации, когда нужно обновить значение какого-то одного атрибута + его значение во flat-таблице. Такая ситуация может возникнуть в cron-скриптах, которые обрабатывают данные от третьего ресурса. Например, мы используем комментарии от <a href="http://disqus.com" target="_blank">Disqus</a> и хотим раз в день забирать от них кол-во комментариев для каждого продукта. Сохранять модель в цикле это самоубийство, потому на помощь приходит Mage_Eav_Model_Entity_Abstract::saveAttribute().<br />
<br />
Предположим, у нас есть атрибут продукта 'disqus_comment_count' и мы как-то (например, с помощью API) получаем от Disqus новые значения комментариев.<br />
<pre class="brush: php">/** @var $productFlatIndexer Mage_Catalog_Model_Product_Flat_Indexer */
$productFlatIndexer = Mage::getModel('catalog/product_flat_indexer'); // модель для обновления значения атрибута во flat-таблице
$storeId = 1; // необходимый store id
/** @var $productCollection Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection */
$productCollection = Mage::getResourceModel('appstore_extension/product_collection');
/** @var $product AppStore_Extension_Model_Product */
foreach ($productCollection as $product) {
$product->setDiscusCommentCount($newCommentValue);
$product->getResource()
->saveAttribute($product, 'disqus_comment_count');
// Обновляем значение атрибута во flat-таблице стора
$productFlatIndexer->updateAttribute('disqus_comment_count', $storeId, $product->getId());
}
</pre>Вот и все. Стоит сказать, что Mage_Catalog_Model_Product_Flat_Indexer::updateAttribute() в цикле это не лучшее решение. Если логика приложения позволяет вынести вызов этого метода из цикла и обновить значение атрибута для пачки продуктов - смело делайте это.Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-38818145719672842452012-11-10T13:34:00.002+02:002012-11-28T18:14:45.025+02:00Установка и настройка Pure-FTPd на FreeBSDНа днях понадобилось устанавливать Pure-FTPd на FreeBSD. Вот небольшой мануал:<br />
<pre class="brush: bash">sudo portsnap fetch update
cd /usr/ports/ftp/pure-ftpd
sudo make config install clean (убедитесь что включена опция "Support for TLS")
sudo ee /usr/local/etc/pure-ftpd.conf
sudo echo pureftpd_enable="YES" >> /etc/rc.conf (см. конфиг ниже)
sudo /usr/local/etc/rc.d/pure-ftpd start
rehash
sudo pure-pw useradd LOGIN -u UID -g GID -d /home/igor/ftp -m (введите ваш LOGIN, UID и GID. UID можно найти в /etc/group, GID такой же как UID)
</pre><br />
FTP сервер настроен. Если вам нужна поддержка SSL/TLS, выполните следующее:<br />
<pre class="brush: bash">sudo mkdir -p /etc/ssl/private
sudo openssl req -x509 -nodes -newkey rsa:1024 -keyout /etc/ssl/private/pure-ftpd.pem -out /etc/ssl/private/pure-ftpd.pem You should skip all questions pressing Enter or enter any appropriate values
sudo chmod 600 /etc/ssl/private/*.pem
sudo ee /usr/local/etc/pure-ftpd.conf Add "TLS 1" at the end of file and save
sudo /usr/local/etc/rc.d/pure-ftpd restart
</pre><br />
Содержимое конфига pure-ftpd.conf:<br />
<pre class="brush: bash">ChrootEveryone yes
TrustedGID 1004
BrokenClientsCompatibility no
MaxClientsNumber 20
Daemonize yes
MaxClientsPerIP 8
VerboseLog no
DisplayDotFiles yes
AnonymousOnly no
NoAnonymous yes
SyslogFacility none
# FortunesFile /usr/share/fortune/zippy
DontResolve yes
# MaxIdleTime 15
PureDB /usr/local/etc/pureftpd.pdb
UnixAuthentication yes
LimitRecursion 10000 8
AnonymousCanCreateDirs no
MaxLoad 4
#AntiWarez yes
Umask 133:022
MinUID 1000
ProhibitDotFilesWrite no
ProhibitDotFilesRead no
AnonymousCantUpload yes
AltLog clf:/var/log/pureftpd.log
#Quota 100000000:1200
MaxDiskUsage 95
CustomerProof yes
NoTruncate yes
IPV4Only yes
</pre><br />
Эти шаги помогут вам установить и запустить ftp-сервер. Для кастомизации читаем "man pure-pw" или отправляемся <a href="http://www.pureftpd.org/project/pure-ftpd/doc" target="_blank">сюда</a>.Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com2tag:blogger.com,1999:blog-6955916244527524020.post-69552103667465965222012-10-12T21:04:00.000+03:002012-10-12T21:08:39.516+03:00Magento: создаем кастомный JS валидаторГод назад я <a href="http://siv-sid.blogspot.com/2011/10/magento-forms-prototype-javascript.html">уже писал</a> о плагине Validation для Prototype, который используется в Magento. Стандартные валидаторы это хорошо. Но что делать, если нужно создать кастомный? Например, очень часто необходимо комбинировать существующими или изменить текст ошибки валидатора.<br />
<br />
Делается это очень просто. Допустим, у нас есть поле, которое мы валидируем стандартными классами:<br />
<pre class="brush: html"><input class="validate-length minimum-length-5 maximum-length-20" name="title" type="text" />
</pre><br />
Для создания кастомного валидатора необходимо убрать валидационный класс "validate-length", но параметры оставить. Они нам еще пригодятся.<br />
<br />
Добавляем валидатор<br />
<pre class="brush: html"><input class="required-title minimum-length-5 maximum-length-20" name="title" type="text" />
<script type="text/javascript">
Validation.add('required-title', 'Please keep your title between 5 - 20 characters.', function (v, elm) {
return Validation.get('validate-length').test(v, elm);
);
</script>
</pre><br />
Мы убрали стандартный валидационный класс "validate-length" и заменили его на кастомный "required-title", в котором использовали логику стандартного валидатора.<br />
<br />
Можно добавить сразу несколько валидаторов<br />
<pre class="brush: html"><input class="required-title minimum-length-5 maximum-length-20" name="title" type="text" />
<textarea class="required-body minimum-length-5 maximum-length-20"name="body" rows="10" cols="70"></textarea>
<script type="text/javascript">
Validation.addAllThese([
[
'required-title',
'Please keep your title between 5 - 20 characters.',
function (v, elm) {return Validation.get('validate-length').test(v, elm)}
],
[
'required-body',
'Please keep your review between 5 - 20 characters.',
function (v, elm) {return Validation.get('validate-length').test(v, elm)}
]
]);
</script>
</pre>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-45089115046126321782012-08-19T13:50:00.000+03:002012-08-19T13:54:42.311+03:00Magento: открываем в нужную табу после редиректаОчень часто при работе с бекендом в Magento требуется сделать редирект на страницу и открыть определенную табу. Например, представим что у нас в табе находится грид, в котором мы производим delete mass action. После выполнения этого действия необходимо вернуться на эту же страницу и открыть табу с гридом. Делается это очень просто.<br />
<br />
Сначала необходимо взять имя табы из хендла, в котором она описана:<br />
<pre class="brush: xml"><action method="addTab">
<name>sms_reply_tab</name>
<block>navoq_magicsms/adminhtml_sms_tab_reply</block>
</action>
</pre>Названием добавленной табы является содержимое тега <name>.<br />
<br />
Теперь можно выполнять редирект:<br />
<pre class="brush: php">$this->_redirect("*/navoq_magicsms/edit", array(
'id' => $sms->getId(),
'active_tab' => 'sms_reply_tab',
));
</pre><br />
Таба откроется благодаря параметру <b>active_tab</b>, в который мы передаем имя табы.Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-53468969820022718612012-08-19T13:25:00.001+03:002012-08-19T13:26:03.940+03:00MageConf 2012<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRGyM8aZs6d-hY1KGLv2xhxwQmlKYLDYhEaTbib4GtDdOQLf_tbv8ClbBkZidWDRzLwRkxH-fPgI2ioZY8kWSz5iOh_TREICYJfAxdREOllLufk4PTrfWnWvKdai6q8-mNC24SzlQkaETx/s1600/0777_MageConf_blogpost_header_v1.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="173" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRGyM8aZs6d-hY1KGLv2xhxwQmlKYLDYhEaTbib4GtDdOQLf_tbv8ClbBkZidWDRzLwRkxH-fPgI2ioZY8kWSz5iOh_TREICYJfAxdREOllLufk4PTrfWnWvKdai6q8-mNC24SzlQkaETx/s400/0777_MageConf_blogpost_header_v1.jpg" /></a></div><br />
15го сентября состоится главное PHP события года в Украине - <a href="http://mageconf.com">MageConf</a>. Будет очень много интересных докладов и воркшопов. Еще можно будет выиграть бесплатный купон для прохождения <a href="http://www.magentocommerce.com/certification/">Magento сертификации</a>. В прошлом году я выиграл лицензию на PhpStorm :)<br />
<br />
Программа конференции - <a href="http://mageconf.com/home/program">http://mageconf.com/home/program</a><br />
Регистрация - <a href="http://mageconf.com/registration">http://mageconf.com/registration</a>Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-26435796894649110762012-08-17T23:53:00.002+03:002012-08-17T23:54:44.016+03:00Фигурные кавычки в JavaScriptСуществуют два С-подобных стиля фигурных кавычек<br />
<pre class="brush: jscript">// Первый
Foo()
{
}
// Второй
Foo() {
}
</pre><br />
Оба очень активно используются в PHP и JavaScript, но меня всегда интересовал вопрос: почему последний вариант наиболее популярен в JavaScript, а первый в PHP/С++/С#. Теперь я знаю ответ: <i>размещение скобки на той же линии после оператора помогает избежать глупых ошибок, возможно благодаря стандарту ECMASrcipt.</i><br />
<br />
В нем говорится следующее:<br />
<blockquote>Certain ECMAScript statements (empty statement, variable statement, expression statement, do-while statement, continue statement, break statement, return statement, and throw statement) must be terminated with semicolons. Such semicolons may always appear explicitly in the source text. For convenience, however, such semicolons may be omitted from the source text in certain situations. These situations are described by saying that semicolons are automatically inserted into the source code token stream in those situations.</blockquote><br />
Приведу небольшой пример для понимания того, что они имеют ввиду:<br />
<pre class="brush: jscript">function FooA() {
return
{
x: 8
};
};
function FooB() {
return {
x: 8
};
};
function Bar() {
console.log(FooA().x);
console.log(FooB().x);
}
</pre><br />
После вызова функции Bar() мы увидим "undefined" и "8". Это произошло благодаря тому, что JavaScript очень умен и поставил точку с запятой после оператора return. То есть интерпретатор никогда не дойдет до нашего анонимного объекта. Почему? "Для удобства", - говорит нам ECMAScript. Наслаждайтесь полученной информацией и пусть программирование на JavaScript приносит вам только удовольствие. Как мне ;)<br />
Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0tag:blogger.com,1999:blog-6955916244527524020.post-24259956565795591592012-08-13T01:15:00.000+03:002012-08-13T08:34:03.715+03:00Navicat Premium Essentials за $9.99Обнаружил что, <a href="http://itunes.apple.com/ru/app/navicat-premium-essentials/id466416967?mt=12" target="_blank">Navicat Premium Essentials</a> продается в Apple App Store за $9.99 вместо $49.99. Очень рекомендую купить эту программу, если вы часто работаете с MySQL/SQLite/Oracle/PostgreSQL. Я знаю что настоящие джедаи используют консоль, но хороший GUI клиент в жизни веб-разработчика просто необходим. Эта программа стоит своих денег.Igorhttp://www.blogger.com/profile/00414646832584791380noreply@blogger.com0