Analitycs

Показаны сообщения с ярлыком код. Показать все сообщения
Показаны сообщения с ярлыком код. Показать все сообщения

суббота, 6 апреля 2013 г.

Как добавить Product-Per-Page для WooCommerce?

Для создания виджета Product-Per-Page (выбор количества продуктов на странице) в WooCommerce нужно использовать вот такой сниппет

Добавлять в functions.php вашей темы

понедельник, 1 октября 2012 г.

Как вывести массив из PHP в JSON без всяких UTF конвертаций?

Если нужно вывести кириллический массив в JSON для последующей обработки в Javascript, генерируя его посредством PHP, да еще и в Windows-1251 (без всяких UTF-8, encoding и прочего)  - да, да - "мсье знает толк в извращениях" (с) ;-) ,

то можно воспользоваться вот такой жуткой конструкцией.

function php2js($a=false)
{
  if (is_null($a)) return 'null';
  if ($a === false) return 'false';
  if ($a === true) return 'true';
  if (is_scalar($a))
  {
    if (is_float($a))
    {
      // Always use "." for floats.
      $a = str_replace(",", ".", strval($a));
    }

    // All scalars are converted to strings to avoid indeterminism.
    // PHP's "1" and 1 are equal for all PHP operators, but
    // JS's "1" and 1 are not. So if we pass "1" or 1 from the PHP backend,
    // we should get the same result in the JS frontend (string).
    // Character replacements for JSON.
    static $jsonReplaces = array(array("\\", "/", "\n", "\t", "\r", "\b", "\f", '"'),
    array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"'));
    return '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $a) . '"';
  }
  $isList = true;
  for ($i = 0, reset($a); $i < count($a); $i++, next($a))
  {
    if (key($a) !== $i)
    {
      $isList = false;
      break;
    }
  }
  $result = array();
  if ($isList)
  {
    foreach ($a as $v) $result[] = php2js($v);
    return '[ ' . join(', ', $result) . ' ]';
  }
  else
  {
    foreach ($a as $k => $v) $result[] = php2js($k).': '.php2js($v);
    return '{ ' . join(', ', $result) . ' }';
  }
}

Страшно, да...  но работает ;-). "Костыли - это кошерно" (с)

Найдено в дебрях PHP.net

суббота, 17 марта 2012 г.

MD5 проверка PHP, Python и MySQL

Несколько лет поймал одну особенность - md5 суммы посчитанные в PHP и MySQL отличались(!!!) - но хоть убей не помню на каких версиях.

Из-за этого, кстати были большие проблемы... Сейчас решил проверить на всякий случай - совпадают.

PHP

php -r "echo md5('domains.txt');"
aeae2f628c54f10054e5c70076e1237b

md5sum

# echo -n "domains.txt" | md5sum
aeae2f628c54f10054e5c70076e1237b 

Python

# echo "import md5;print md5.md5(\"domains.txt\").hexdigest()" | python
aeae2f628c54f10054e5c70076e1237b

MySQL

SELECT MD5('domains.txt');

aeae2f628c54f10054e5c70076e1237b

Уф, отпустило ;-) Приступ паранойи навеян вот этой заметкой.

четверг, 15 марта 2012 г.

Logging в Tornado

После большого рефакторинга проекта на Tornado пропало логгирование в stdout. То есть ошибки писались только в stderr, который выводился в лог сидящего уровнем выше сервиса  Supervisord (который за сервисом, собственно говоря, и наблюдал).

После небольшой отладки выяснилось, что проблема была в одной строчке, случайно удаленной из проекта

tornado.options.parse_command_line()

То есть должно быть примерно так

def main(): 
    application = tornado.web.Application([ 
        (r"/", MainHandler), 
    ]) 
    # this line will setup default logging no matter if you use command options 
    tornado.options.parse_command_line()
    logging.info("starting torando web server") 
    http_server = tornado.httpserver.HTTPServer(application) 
    http_server.listen(8888) # hardcoded port 
    tornado.ioloop.IOLoop.instance().start() 

После восстановления строчки-"беглянки" лог торнадовского приложения выглядит как положено


mneradkov@mneradkov-thinkpad:~/workspace/mysuperproject$ ./webapp.py 
[I 120315 16:33:28 mixins:28] [INFO] Directory [/home/mneradkov/workspace/mysuperproject]
[I 120315 16:33:28 mixins:28] [INFO] Connected to MySQL [xx.xx.xx.xx:xxxx]
[I 120315 16:33:28 mixins:28] [INFO] Connected to memcache [127.0.0.1:11211]
[I 120315 16:33:28 mixins:28] [INFO] 
[I 120315 16:33:28 mixins:28] [INFO] Server [My Super Server] version [X.Xalpha] binded to [127.0.0.1:11100]
[I 120315 16:33:28 webapp:37] Starting I/O loop to serve requests...

пятница, 24 февраля 2012 г.

Tornado AsyncHTTPTestCase: AssertionError: Async operation timed out after 5 seconds

Тестирование в Торнадо (особенно асинхронных сервисов) имеет много неочевидных нюансов.

Ситуация

Если ваш сервис внутри асинхронных обработчиков сам создает асинхронные задачи - например, для того чтобы забрать что-то с внешнего сервиса - в стиле

class MyAsyncHandler(tornado.web.RequestHandler):

    @tornado.web.asynchronous
    def get(self, *args, **kwargs):
        try:
            # тут чего то берем с внешнего сервера
            self.auth_request({})
        except Exception, ex:
            self.error('Async - %s'%ex)
            self.reply(None)


    def auth_request(self, params, callback=None):
        url = self.application.settings['external_service'] + '?' + urlencode(params)

        if not callback:
            callback = self._on_load

        http = httpclient.AsyncHTTPClient()
        http.fetch(url, callback, validate_cert=False)

    def _on_load(self, response):
        # обрабатываем полученные результат        
        pass

То при написании юнит-тестов через AsyncHTTPTestCase в подобном ключе по официальной документации(!!!)

class MyTestServer(AsyncHTTPTestCase):
    def get_host(self):
        return 'http://%s:%d'%(SERVER['host'], SERVER['port'])
    
    def get_app(self):
        myserver = MyServer()
        myserver.run(SERVER['host'], SERVER['port'])        

        return myserver.app # приложение Tornado
    
    def get_response_obj(self, url, **kwargs):
        if len(kwargs) > 0:
            url += '?' + urllib.urlencode(kwargs)
        
        logging.info('Request [%s]'%url)

        self.http_client.fetch(url, self.stop)
        response = self.wait()
        
        print response

        if response.body: 
            return response.body
        
        return None

При запуске тестов начинаем ловить потрясающие спецэффекты,

Traceback (most recent call last): 
  File "/home/xxxx/test_wait.py", line 9, in test_1 
    self.wait(timeout = 5) 
  File "/xxxxx/site-packages/tornado/ 
testing.py", line 169, in timeout_func 
    timeout) 
AssertionError: Async operation timed out after 5 seconds 

Лечится это добавлением в класс теста переопределенного метода get_new_ioloop

class MyTestServer(AsyncHTTPTestCase):
    ....
    
    def get_new_ioloop(self): 
        return ioloop.IOLoop.instance()  

Такие дела...

Тема в гуглогруппеGist с примером

суббота, 11 февраля 2012 г.

Как проверить string ли переменная в Python? - isinstance(val, basestring)

Когда нужно проверить - строка ли переменная в Python - есть небольшой подводный камень, о котором иногда забывают...

Все ли помнят что строки в питоне разные? (например, str и  unicode), так что для общей проверки нужно использовать isinstance(val, basestring):

 Например

$ python
Python 2.7.1 (r271:86832, Jul 31 2011, 19:30:53) 
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

>>> isinstance('abcd', str)
True
>>> isinstance('abcd', basestring)
True
>>> isinstance(u'abcd', str)
False
>>> isinstance(u'abcd', basestring)
True

пятница, 10 февраля 2012 г.

Как запускать периодические задачи на Tornado? PeriodicCallback!

Продолжая тему периодических задач в Twisted, в Tornado это делается немного по другому - с помощью специального tornado.ioloop.PeriodicCallback.

Примерчик - если кому пригодится

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tornado.web
import tornado.httpserver
import tornado.ioloop

PERIOD = 30 # в минутах

class MyApplication(tornado.web.Application):

  def period_run(self):
    # чего-то тут делаем
    pass

...

app = MyApplication(handlers=urls, **settings)

http_server = tornado.httpserver.HTTPServer(app, xheaders=True)
 
loop = tornado.ioloop.IOLoop.instance()
period_cbk = tornado.ioloop.PeriodicCallback(app.period_run, 1000*60*PERIOD, loop)

period_cbk.start()

loop.start()

вторник, 31 января 2012 г.

Python/PHP - запятая - казнить нельзя помиловать

Сегодня ловлю баг в сервисе - в JSON-результате вместо поля с integer  выдается список элементов-integer, то есть вместо

data : {
  'id' : 1
}

упорно получаю

data : {
  'id' : [1]
}

Перепроверяю код 20 раз - ничего не могу понять... вроде все правильно, должен быть идентификатор ОДИН, но хоть ты тресни - список. Подвисаю на несколько минут, иду попить кофе, снова смотрю в код...

Через несколько минут доходит - начинаю истерически хохотать

def bla-bla(self, args): 
    data = {
      'bla-bla' : bla_bla,
      'name' : name,
      'id' : temp_id,
    }
  return data


Правлю один символ - все работает. После хохота начинаю вспоминать хорошего PHP team-leader, с которым работал много-много лет назад.

Он был большим аккуратистом в плане чистоты форматирования кода и железнобетонно приучил меня расставлять меня запятые в ассоциативных массивах в КАЖДОМ элементе, включая последний.  Так как в случае необходимости быстро переставить элементы в коде - не пришлось бы искать потенциальную ошибку c пропущенной запятой.

А для Python - это собственно - сокращенный формат списка-последовательности.

Так что вот вам прекрасный пример того, что старые привычки часто вредят, а иногда - вредят ОЧЕНЬ сильно. "Казнить нельзя помиловать" (с)   ;-)

Ну и верный вариант - БЕЗ запятой ;-)

def bla-bla(self, args): 
    data = {
      'bla-bla' : bla_bla,
      'name' : name,
      'id' : temp_id
    }
  return data

пятница, 27 января 2012 г.

Python implode/join - TypeError: sequence item 0: expected string, int found

Одна из частых функций в PHP проектах - implode/explode.

На Python (по идее/по мануалу) это делается так

tmp = [1,2,3,4,5]
','.join( tmp )

Но в результате - получаем ошибку TypeError: sequence item 0: expected string, int found

Проблема - в том, что у нас числа, а не строки

 В итоге - нужно так

','.join( map( str, tmp ) )
'1,2,3,4,5'

Но вот пресловутое неявное приведение типов-то в PHP как помогает, а? ;-)

вторник, 17 января 2012 г.

Email от PHPBB падает в spam

Диагноз и причина

Оповещения от русской версии PHPBB падают в спам.

X-Amavis-Alert: BAD HEADER SECTION, Non-encoded 8-bit data (char D3 hex): 
   Subject: 323342345344356354353345355350345 356341 
   356[...] 
X-Spam-Flag: NO 
X-Spam-Score: 4.301 
X-Spam-Level: **** 
X-Spam-Status: No, score=4.301 tagged_above=-10 required=5 
   tests=[BAYES_50=0.8, RCVD_IN_DNSWL_NONE=-0.0001, 
   SUBJECT_NEEDS_ENCODING=0.049, SUBJ_ILLEGAL_CHARS=1.518, 
   TO_NO_BRKTS_MSFT=1.934] autolearn=no

subject уходит с сервера в iso-8859-1/quoted. В этой кодировке русских букв нет.

Subject: 
   =?iso-8859-1?Q?=D3=E2=E5=E4=EE=EC=EB=E5=ED=E8=E5_=EE=E1_=EE=F2=E2?= 
   =?iso-8859-1?Q?=E5=F2=E5_=E2_=F2=E5=EC=E5_-_=D7=F2=EE_=ED=E5_=ED=F0=E0=E2?= 
   =?iso-8859-1?Q?=E8=F2=F1=FF_=ED=E0_=F1=E0=E9=F2=E5?=

Хотя тело сообщения - нормально в Windows-1251

MIME-Version: 1.0
Content-type: text/plain; charset=windows-1251 
Content-transfer-encoding: 8bit 

Лечение


Нужно изменить кодировку Subject на необходимую с русскими буквами(win1251, utf8, koi8r).

файл

PHPBB_DIR/include/emailer.php

после строчки

$this->subject = (($this->subject != '') ? $this->subject : 'No Subject');

Добавляется

$this->subject = '=?'.trim($lang['ENCODING']).'?B?'.base64_encode($this->subject).'?=';

И после - вуаля

X-Spam-Flag: NO 
X-Spam-Score: 0.034 
X-Spam-Level: 
X-Spam-Status: No, score=0.034 tagged_above=-10 required=4 
   tests=[BAYES_00=-1.9, RCVD_IN_DNSWL_NONE=-0.0001, 
   TO_NO_BRKTS_MSFT=1.934] autolearn=no

По материалам этой темы, спасибо коллеге BW4ever за баг-репорт

понедельник, 9 января 2012 г.

Как подключить Яндекс-Спеллер (Yandex-Speller) к PHPBB?

Есть у Яндекс одна хорошая штука, до которой у меня долго не доходили руки...  а именно - проверка орфографии в формочках ввода - Яндекс-Спеллер. Оказалось, что зря откладывал - не так сложно.

Как подключить к форуму PHPBB2 (как модуль на RunCMS)? По идее - в чистом PHPBB2/3 не должно сильно отличаться, разве что файлы  будут другие, скорей всего...

1) Разместить на сайте папку с самим спеллером в /class/xoopsform/yandex_speller/ Затем

2) файл include/viewtopic_quickreply.php
 
$addon_html = '
<script type="text/javascript" src="'.XOOPS_URL.'/class/xoopsform/yandex_speller/spell.js"></script>

// YandexSpeller 

var speller = 

new Speller({ url:"/class/xoopsform/yandex_speller", lang:"ru", options:Speller.IGNORE_URLS });
// Настройка параметров проверки http://api.yandex.ru/speller/doc/dg/reference/speller-js.xml  

<button name="cmdSpell_message" onclick="speller.check([document.getElementById(\'message\')])" type="button">'._CORE_CHECK_ORPHO.'</button>
';

$template->assign_vars(array(
  'U_POST_SQR_TOPIC' => 'javascript:sqr_show_hide();',
  'SQR_IMG' => $images['quickreply'],
  'L_POST_SQR_TOPIC' => $lang['Show_hide_quick_reply_form'],

  'L_EMPTY_MESSAGE' => $lang['Empty_message'],
  'L_QUICK_REPLY' => $lang['Quick_Reply'],
  'L_USERNAME' => $lang['Username'],
  'L_NO_TEXT_SELECTED' => $lang['Qreply_no_text_selected'],
  'L_SUBJECT' => $lang['Subject'],
  'L_MESSAGE_BODY' => $lang['Message_body'],
  'L_PREVIEW' => $lang['Preview'],
  'L_SUBMIT' => $lang['Submit'],
  'S_POST_ACTION' => append_sid("posting.php"),
  'S_HIDDEN_FORM_FIELDS' => $hidden_form_fields,
  'ADDON_HTML' => $addon_html,
  )
);

где _CORE_CHECK_ORPHO - наш новый языковой дефайн, 'ADDON_HTML' => $addon_html, - добавленная строчка в код и шаблон.

3) правим шаблон

Особо расписывать не вижу смысла - все просто как грабли.


Как подключить спеллер к формам - читать тут.
Полное API
Навеяно вот этим хаком

пятница, 6 января 2012 г.

Как оптимизировать RunCMS? или Оптимизация CMS для начинающих

Решил перевыложить свою старую статью - она местами уже потеряла актуальность, да и RunCMS практически уже умерла, к сожалению - оставив после себя несколько веток-форков -на одной из которых и работает ScaleModels.ru, но люди спрашивают. Увы - некоторые вещи, приведенные тут уже устарели за несколько лет, некоторые - спорны, но  пусть будет в этом блоге - до кучи. 

RunCMS - достаточно неплохая система, дружественная для новичка в Web, которая позволяет худо-бедно, но решить большинство типовых задач для небольшого сайта одним приемом - «из коробки». Однако сайты имеют свойство либо умирать, либо развиваться, и в случае когда ваш ресурс выходит из категории «homepage про меня и моего хомячка Вову» (c) - начинаются проблемы с производительностью. Ситуация осложняется тем, что большие и серьезные CMS (такие как Drupal или Joomla) давно прошли этот этап «детских болезней» и способы решения подобных проблем для них давно известны и доступны. К сожалению, крупных проектов на RunCMS встречается не много, и информации о ней очень мало. На правах бывшего coreteam разработчика RunCMS я хочу рассказать о способах увеличения ее производительности.

Данная статья не предназначена для полных новичков - подразумевается, что вы в состоянии отличить PHP от Perl, способны разбираться в чужом коде, и примерно представляете архитектуру RunCMS (или XOOPS-like систем). Желательно, чтобы вы знали что такое my.cnf и где его искать ;-)

Есть два варианта хостинга - обычный и VPS/VDS.

Обычный хостинг

В плане оптимизаций - это жопа (если вкратце). Особенно если у вас провайдер, который не хочет ставить дополнительный софт и его настраивать. Начиная этак от 500 уников в день - готовьтесь к прессингу с его стороны - «давайте-ка переедем на выделенный сервер» (с) Один мой прошлый хостер на просьбу установить поддержку mbstring на свой шаред-хостинг спросил - «А зачем он вам нужен?» Но в принципе, мигрировать на свой сервер - это логичный вариант.

Что можно сделать для оптимизации RunCMS непосредственно в ней самой на SHARED хостинге?

MySQL таблицы для сессий

Находим MySQL таблицу sessions и конвертируем ее в тип MEMORY (если стоит PHPBB - то такая судьба должна постигнуть и phpbb_session) Этим мы выиграем небольшое количество скорости - но на КАЖДОЙ страницы сайта. Единственный минус - после перезагрузки сервера всем пользователям прийдется перелогиниваться, но результат того стоит.

Кэш страниц для гостей-анонимусов

Включаем кэширование страниц для гостей на как можно большее время - однако учитывайте, что для этого нужно хотя бы 50-100 мб свободного места на диске?

Включаем кэширование MySQL средствами класса DB

class/database/mysql.php

<?php

    var $file_cache = true;

?>


Это то, что можно было сделать нормальными ШТАТНЫМИ способами RunCMS, а теперь начинаем экспериментировать

Файл-кэш SELECT запросов

Изучаем код сайта и модулей, добавляя для тяжелых запросов кэширование через метод

<?php

$db->query($sql, false, false, ‘имя_файла’,  время_кэширования)

?>


ставя подходящие значения. Учитывайте только то, что кэш отрабатает только в том случае если результат запроса будет прогнан через fetch_row или fetch_array , а вот fetch_object в текущей версии не кэшируется?

Скомпилированное ядро

Включаем экспериментальное компилированное ядро (класс RCCoreApi) файл class/core.php

<?php

var $compiling = true;

?>

Данное «ядро» была введено мной в версии 1.6.1 - оно позволяет сэкономить примерно 6 SQL запросов на каждой странице, но несколько повысит нагрузку на PHP.

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

Вообще если вы любознательны и способны экспериментировать и разбираться с чужим кодом - внимательно изучайте стандартные классы RunCMS 1.6.1 и 1.6.2.

Немного яда и кидания какашек

Несмотря на то, что netshark просто вышвырнул меня из разработки RunCMS и вычеркнул из списка живых team, а впоследствии - передал код в лапы Farsus, который ее успешно и угробил :-) - в коде осталось много экспериментальных неофициальных настроек для увеличения производительности сайта

Например -

Кэш деревьев

Установить в классе xoopstree.php

<?php

define('RC_TREE_CACHE', 1); 

?>

это сильно поможет при работе с большими деревьями категорий - в таких модулях, как news, например

Кэширование блоков

Если внимательно посмотреть на табличку newblocks - вы увидите поле cache_time. При установке его в целое положительное значение N, блок будет закэширован на N минут. Обратите внимание - кэшировать имеет смысл только те блоки, которые выглядят ОДИНАКОВО для всех пользователей - если содержимое блока будет разным, то возможен конфликт.

RCCache и RCCachedPage

Использование классов RCCache и RCCachedPage, написанных совместно с Shurik2k5. В репозитории RunLiveCMS можно найти версии этих классов не только для файлового кэша (как в RunCMS), так и в базу MySQL и Memcache - что предпочтительней, с (или без) serializing данных

Настройка выделенного сервера (VPS/VDS)

Если же вы понимаете, что сайт будет расти дальше - то предстоит часть вторая - настройка выделенного сервера.

Ниже только ОБЩИЕ рекомендации, куда стоит смотреть
  • Имеет смысл поставить PHP акселератор - на моем сервере стоит Zend Optimizer + eAccelerator. 
  • Тщательно настроить сервер MySQL - не забывайте про кэш запросов самого сервера и ограничение по времени медленных запросов
  • После настройки Apache - отключите access логи на картинках, оставив для них только error
  • Имеет смысл избавиться от .htaccess файлов, снеся все редиректы и настройки непосредственно в httpd.conf
  • Повесить на front-end (перед Apache) nginx и настройте его на отдачу статических файлов (таких как картинки, CSS, JS) - либо вообще отказаться в пользу Nginx + PHP-FASTCGI.
  • Используйте RAM-диск для критичных файловых кэшей - в моем случае туда сохраняется файловый кэш класса DB, compiled_kernel, блоки и кэши некоторых страниц
  • Обязательно используйте программы top, mtop и apachetop для мониторинга своей системы - ведь лучше вас ее не знает никто

По настройке выделенных серверов написано достаточно много, в частности тут - про Nginx, PHP, Memcache и тут про MySQL и насморки.


В моем случае - самый сильный выигрыш был от введения (по порядку важности)
  • Гостевое кэширование
  • Nginx
  • настройка MySQL
  • RAM диски на кэши кусков страниц, блоков и темплейтов форума /modules/forum(ну или phpBB2)/cache (всего около 200МБ RAM) - у меня форум, статьи и главная страница - основная нагрузка
  • eAccelerator
  • Таблицы сессии в MySQL MEMORY

Все остальное, честно говоря - из разряда "экономить на спичках" - не помешает, но без этого можно обойтись

Возможно, кому-то эти советы покажутся детским садом и «мурзилкой», но кому-то - сэкономят несколько минут «гугления» и чесания в затылке.

Изначально было опубликовано тут.

суббота, 3 декабря 2011 г.

Как сделать sharding картинок в PHP между несколькими серверами?

Сегодня небольшой хак - боюсь, что скоро их будет тут больше, чем методически грамотных решений. ;-(

Когда вам нужно разшардить картинки/статические ресурсы по разным поддоменам для ускорения загрузки - можно использовать очень-очень грязный хак.

Дано

4 поддомена:
  • http://st1.xxx.ru
  • http://st2.xxx.ru
  • http://st3.xxx.ru
  • http://s4.xxx.ru
По ним и нужно раскидывать превьюшки - особенно это актуально, когда их много на страницах.

Функция получения url для превьюшки картинки после небольшой доработки выглядит так

 public function thumb_url($file = false) {
  $path = $this->path_by_file($file);
  $url = CMS_URL.'/'.$path.'/thumbs/'.$file;
  
  $key = md5($url) & 3;
  
  $static_url = 'http://st'.($key+1).'.xxx.ru/'.$path.'/thumbs/'.$file;
  
  return $static_url;
 }

Чем плох этот вариант?

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

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

Короче - не делайте так.

А как правильно?

А как в том случае сделать подобное распределение методически грамотно  - это тема для отдельного поста. Может  быть и напишу, но позже.

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

Как включить показ ошибок PHP в браузере?

Классический вопрос всех похапешников - "как показать ошибки PHP скрипта в окне браузера"

error_reporting(E_ALL);
ini_set("display_errors", 1);

P.S. Причем сам постоянно забываю, как точно пишется эта конструкция - особенно после переключения с языка на язык. Ненавижу переключать контекст мозга ;-)

среда, 23 ноября 2011 г.

PHP - хак быстрого логгирования в файл


Если вам в зубы выдали какой-то очередной супер-мега-пупер проект на PHP, в дебрях которого нет возможности и желания разбираться, однако нужно что-то быстро пофиксить с отладкой, (причем доступа к php error_log нету) - то можно быстро воткнуть небольшой полезный хак.

Это дополнительный метод в класс (или просто - функция) с минимальным количеством кода.

   function log($msg) {
       file_put_contents(SUPER_CMS_ROOT_PATH.'/cache/php-debug.log',"\n".$msg, FILE_APPEND);
   }

Соотвественно, после отладки ее можно либо убить, либо оставить, закомментировав содержимое.

P.S. И да -про некошерность данного метода и недопустимости этого при учете всех методик программирования - я в курсе, не нужно возбуждаться. ;-)
Просто нужно пофиксить и отладить - БЫСТРО.

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

Как на python проверить, открыт ли какой-то port?


from socket import socket, gethostbyname, AF_INET, SOCK_STREAM

target = "localhost"
targetIP = gethostbyname(target)
port = 80
s = socket(AF_INET, SOCK_STREAM)

result = s.connect_ex((targetIP, port))

if(result == 0) :
 print 'Port %d is open' % (port,)
s.close()

понедельник, 14 ноября 2011 г.

Как реализовать периодические задачи в Twisted? (cron vs LoopingCall)

Первое что делают все программисты на Twisted, чтобы сделать периодические задачи - это лезут в cron. Я сам так раньше делал - и, к сожалению, не было никого рядом, чтобы стукнуть меня бамбуковой палкой по рукам.

Есть же методически грамотное, ВСТРОЕННОЕ решение - и нашел я его сегодня случайно, когда искал совершенно другую вещь.

from twisted.internet.task import LoopingCall

#==============================================================================
class MySuperServer(MyServer, twisted.web.server.Site):
    '''
    мой собственный веб-сервер, с блекджеком и шлюхами ;-)
    '''

    def __init__(self):
        lp = LoopingCall(self.checkStatus)
        lp.start(1.0) # период в секундах    

    def checkStatus(self):
        #self._log('Looping call')
        pass

среда, 9 ноября 2011 г.

PHP header("HTTP/1.0 404 Not Found") показывает пустую страницу

При отправке из PHP скрипта HTTP ошибки 404 - показывается белая страница, хотя в настройках вашего веб сервера явно задана собственная красивая страничка для этой ошибки.

Как лечить?

Просто считать содержимое файла с красивой 404 ошибкой и вывести его HTML в поток - примерно так
if ($is404) {
 $html = file_get_contents(YOUR_ROOT_PATH.'/404.htm');
 header("HTTP/1.0 404 Not Found");
 echo $html;
 exit();
}

четверг, 3 ноября 2011 г.

Python, Twisted, logging - Как логгировать несколько процессов/демонов в один файл?

Давайте поговорим немного про логгирование в Python.

Да, меня это ДЕЙСТВИТЕЛЬНО беспокоит, и я хочу об этом поговорить ;-)

Совсем недавно столкнулся с тем, что достаточно серьезный проект при портировании с Linux на MacOS потерял логгирование. Ну нет лог файла - и все тут. Полез в код разбираться - выяснилось, что разработчики очень любили изобретать велосипеды и делали логгирование через файл, открытый ручками и обычную запись туда переменных - прямо в файл. Все это работало... до поры до времени. Пришлось на скору руку переписывать.

В общем, мораль сей басни такова...

Есть в Python СТАНДАРТНЫЙ модуль logging  - его и нужно использовать для разного типа логгирования. Батарейка-то уже включена ;-)

Чтобы пример кода не был совсем тривиальным - приведу решение для той ситуации, когда у нас есть несколько многопроцессных РАЗНЫХ демонов на Twisted на одной машине, а еще и cron-скрипт запускается время от времени. И всю жизнедеятельность этого дурдома нужно логгировать в один файл

import os
import logging
from twisted.python import log                                                                                   

CURRENT_PATH = os.path.realpath(os.path.dirname(__file__))

# Все это "по уму" обычно запихивается в нормальный конфиг-файл
LOG_DIR = os.path.join(CURRENT_PATH, 'logs')                            
LOG_FILE = os.path.join(LOG_DIR, 'super-puper.log')  
LOG_LEVEL = logging.INFO
 
# инициализируем
FORMAT = ('%(asctime)-15s %(levelname)s %(message)s') 

if not os.path.exists(LOG_DIR):
    os.mkdir(LOG_DIR) 
 
logging.basicConfig(format=FORMAT, filename=LOG_FILE, handler=logging.handlers.RotatingFileHandler)   
observer = log.PythonLoggingObserver(loggerName='twisted')
observer.start()
observer.logger.setLevel(LOG_LEVEL)  

# класс-примесь для логгирования - в принципе - необязательно, реализация может быть разная - хоть функциями
class BasicLogClass():

    def _log(self, msg):
        log.msg(msg, logLevel=logging.INFO)

    def _error(self, msg):
        log.msg(msg, logLevel=logging.ERROR)

    def _warning(self, msg):
        log.msg(msg, logLevel=logging.WARNING)

В данном варианте работоспособность не проверял, но логика применения именно такая - выкусил из работающего проекта.

Кстати, если у кроновских скриптов нет использования twisted'овского reactor, то могуть быть проблемы, нужно подумать. Как вариант - использовать развилку в инициализации и логгировании без применения observer, но не уверен.

суббота, 22 октября 2011 г.

Немного о сборке программ с dylib в MacOS

Узнать, какие зависимости у файла к библиотекам

$ otool -L yourFile

На примере злополучной библиотеки MySQL для Qt

$ otool -L /Developer/Applications/Qt/plugins/sqldrivers/libqsqlmysql.dylib
/Developer/Applications/Qt/plugins/sqldrivers/libqsqlmysql.dylib:
 libqsqlmysql.dylib (compatibility version 0.0.0, current version 0.0.0)
 /opt/local/lib/mysql5/mysql/libmysqlclient_r.16.dylib (compatibility version 17.0.0, current version 17.0.0)
 QtSql.framework/Versions/4/QtSql (compatibility version 4.6.0, current version 4.6.2)
 QtCore.framework/Versions/4/QtCore (compatibility version 4.6.0, current version 4.6.2)
 /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
 /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 438.0.0)
 /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.11)

Включить показ динамически загружаемых библиотек runtime

# export DYLD_PRINT_LIBRARIES=1

В результате

$ nano dylib.log
dyld: loaded: /usr/bin/nano
dyld: loaded: /usr/lib/libncurses.5.4.dylib
dyld: loaded: /usr/lib/libSystem.B.dylib
dyld: loaded: /usr/lib/system/libmathCommon.A.dylib

Потом, разумеется, не забываем выключить в 0.

Изменить путь линкуемой библиотеки

$ install_name_tool -change /Developer/Applications/Qt/plugins/sqldrivers/libqsqlmysql.dylib @executable_path/libqsqlmysql.dylib ./dist/MyApp.app/Contents/MacOS/MyApp



Спасибо MikhailEdoshin