Иногда на сервере происходят сошибки типа "mysql server has gone away". Ошибка весьма достадная, тем что возникает она не всегда а только иногда. При этом, путем эмпирических действий удалось выяснить, что такое сообщение вызывается если PHP скрипт какое то время чем то сильно был занят и при этом не обращается к базе данных. Когда после некоторго периода бездействия скрипт, наконец хочет что-либо записать в базу данных то обнаруживается, что ссылка на соединение с базой данных уже мертва и все последующие запросы вылетают с ошибкой "mysql server has gone away". Понятно, что соединение с базой данных рвется по некоему таймауту, но вот по какому и как его увеличить?
Оказалось, что дело в переменной wait_timeout которая живет в /etc/my.conf. В моем случае она оказалась установленной в 30 секунд, поэтому, если во время выполнения скрипта между запросами оказывается промежуток более тридцати секунд - соединение рвется и больше не восстанавливается.
Изменить это значение можно либо поправив my.conf, либо после установления соединения выполнив "SET wait_timeout=1000".
Коментариев: 1
Понадобилось посчитать дистанцию между двумя точками заданными в GPS координатах. В PHP реализация такой фунции выглядит следующим образом:
function calcMiles ($Lat1, $Lon1, $Lat2, $Lon2){
return 3958.75 * acos( sin($Lat1/57.2958) * sin($Lat2/57.2958) + cos($Lat1/57.2958) * cos($Lat2/57.2958) * cos($Lon2/57.2958 - $Lon1/57.2958));
}
Ответ получается в милях, для перевода в метры, полученное значение нужно ещё умножить на 1609.344.
Код взял с phpclasses.com. Кстати, глядя на код оригинального класса можно сделать вывод, что автор обладает хорошим чувством юмора - он реализовал вывод результата в десятках различных единиц, включая нанометры, парсеки и световые года.
Оставить комментарий
Я уже писал, что нужно сделать, чтобы заменить в коде Posix регулярные выражения, которые в PHP 5.3 стали deprecated, на Perl совместимые. Оказывается Rerl совместимые регулярные выражения поддерживают литералы используемые в Posix такие как [[:allnum:]], [[:space:]] и т.д. В одном месте чужёго кода заметил это, и вот дошли руки проверить на практике: конструкция вида preg_match('/[[:space:]]/','df d') работает идентично конструкции вида preg_match('/\s/','df d') т.е. совсем не нужно при замене ereg на preg_match заменять еще и литералы, достаточно добавить слева и справа слеши "/". Кстати кроме слешей можно использовать любой другой символ, например "#" это полезно когда в регулярном выражении уже есть слеши - чтобы их не экранировать и не ухудшать читаемость кода.
Оставить комментарий
Вызов статических методов классов — прекрасная альтернатива обычным функциям - и класс создавать не нужно и принципы объектно-ориентированного программирования не нарушаются. Однако, в реализации статических методов в PHP есть несколько проблем - статические методы не всегда корректно наследуются, поэтому, чтобы нивелировать неудобства статических методов в PHP часто используют синглетоны - обертывают денимические методы статическими обертками.
Однако, создание синглетона операция довольно ресурсоёмкая — проверить создан ли экземпляр класса, создать экземпляр класса если он не создан, вызвать на нём нужный метод. Насколько это сказывается на бытродействии я и решил сегодня проверить.
Для оценки скорости доступа к методам синглетона я сделал небольшой скрипт. Скрипт генерирует тестовые последовательности вызовов функций в различных вариантах - и сравнивает время вызовов этих последовательностей.
Вот исходный код скрипта.
Результат работы скрипта представлен в таблице ниже:
generation_time |
11.7576429844 |
php_version |
5.3.2-1ubuntu4.9 |
Case |
try 1 |
try 2 |
try 3 |
try 4 |
middle |
Same plain function called 10000 times |
0.0166 |
0.0187 |
0.0183 |
0.0135 |
0.0168 |
Different plain functions called 10000 times |
0.0163 |
0.0221 |
0.018 |
0.0163 |
0.0182 |
Same static method 10000 times |
0.0193 |
0.0265 |
0.0206 |
0.0194 |
0.0214 |
Different static methods called 10000 times |
0.0221 |
0.0283 |
0.0229 |
0.0229 |
0.024 |
Static methods of different classes called 10000 times |
0.0236 |
0.0279 |
0.0251 |
0.0242 |
0.0252 |
Same dynamic method called 10000 times |
0.0166 |
0.0193 |
0.0169 |
0.0169 |
0.0174 |
Different class methods called 10000 times |
0.02 |
0.02 |
0.0202 |
0.02 |
0.02 |
Singleton method called 10000 times |
0.0403 |
0.0446 |
0.0409 |
0.041 |
0.0417 |
Different singletons called 10000 times |
0.0562 |
0.0631 |
0.0548 |
0.0572 |
0.0578 |
В тестовом скрипте, я сравнил обычные PHP функции, статические методы класса, обычные методы и методы синглетона.
Результаты тестирования показали, что вызов методов синглетона, примерно в два - четыре раза медленнее, чем вызов обычных методов, что, в принципе, меня порадовало — я ожидал, что будет хуже.
При этом выяснилось несколько дополнительных вещей: cтатические вызовы функций объявленных без модификатора "static" примерно в два раза медленнее, чем с ним, cтатические методы немного медленнее, чем динамические.
Так что, в принципе, синглетоны вполне можно использовать, особенно в случаях, когда быстродействие не сильно критично.
Оставить комментарий
Два дня бился со странным глюком - в скрипте в определенном месте отваливается соединение с базой данных - выдает "MySQL server is gone away". При этом отваливается всегда в одном и том же месте после куска кода с большим объемом вычислений, но без единого вызова базы данных. И только на сервере - на локальном компьютере всё тип-топ. Путем небольших, почти шаманских манипуляций с кодом удалось проблему купировать, правда неизвестно, где она может всплыть в дальнейшем. Налицо проблема в самом интерпретаторе PHP.
Upd: Нашел источник проблемы
Такое себе позволял только PHP4. Похоже, PHP5 вступил в ту стадию зрелости за которой уже наступает старческий маразм...
Коментариев: 3
Не знаю, сталкивался ли кто или это мне повезло, но вчера поймал загадочный баг, похоже, что в движке PHP, хотя, пока до конца не уверен.
Предыстория такая - работаю над проектом, над которым потрудились индийские программисты. Сам проект сделан на движке Symphony - добротный такой движок, вполне приятный. В качестве библиотеки работы с базой данных используется Creole - тоже довольно приятная штука. В качестве ORM используется Propel — штука громоздкая, но тем не менее тоже весьма мощная и простая. Вообще Symphony показался мне весьма неплохим движком, весьма простым и удобным. Наверно, потому, что он почти один в один копирует Ruby on Rails - те же хелперы, тот же yml в конфигах, такой же подход к MVC - в общем очень похоже.
Так вот, доблестные индийские программисты, вместо того, чтобы использовать оснастку из Symphony навставляли везде mysql_connect и mysql_query. И все бы ничего, но mysql_connect у них вставляется ВЕЗДЕ перед mysql_query — в одном скрипте может быть создано несколько десятков соединений, а каждый mysql_query дополняется проверкой "or die(mysql_error())", поэтому любая ошибка в sql ведет к скоропостижной кончине всего скрипта.
Чтобы хоть как то исправить это безобразие я сначала удалил все mysql_connect. Затем, все mysql_query поиском и заменой заменил на собственную функцию, использующую оснастку creole, поместил её в глобально видимый класс myTools::query():
static function query($sql){
$connection = Propel::getConnection();
$r = $connection->executeQuery($sql);
return $r->getResource();
}
Странное дело код ведь тривиальный, но ресурс возвращаемый этой функцией оказался невалидным! Причем внутри функции ресурс еще остаётся рабочим а возвращённый во вне оказывается сломанным. Тот же код ВНЕ функции идеально работает, но как только выношу его в функцию — перестаёт работать.
Что это — глюк самого PHP или глюк Simphony я так и не понял, пришлось немного модифицировать класс MySQLConnection и добавить туда метод возвращающий MySQL ресурс непосредственно, минуя всякие PHP обёртки, но осадочек все же остался...
Оставить комментарий
Не спорю, штука прикольная, но уж больно архитектура у него специфическая:
-
нет OOP: несмотря на то, что в системе вроде бы присутствуют классы - основная функциональнасть реализована на функциях;
-
плохие привычки программирования: широко используются глобальные переменные;
-
JavaScript ад: бардак со скриптами - доходит до того что каждый плагин тянет за собой собственный jquery или prototype;
-
тем полно, но все кривые: темы сделаны по-идиотски - нет единого соглашения для создания тем, нет четкого разделения навигации и шаблонов - по сути вся навигация и функционал заложены в теме - а это половина движка;
-
нет MVC: собственно нет шаблонов вообще - логика перемешана с HTML так не кодируют уже давно;
-
SQL: движёк для работы с базой данных неплох - но он негибкий, заточен только под WP и только под MySQL;
-
CMS: виджеты можно настраивать для всех страниц сразу, выборочно никак нельзя;
-
низкое быстродействие: из-за навороченной системы фильтров и хуков в финале система получается тормозная и прожорливая;
-
разработчики не подозревали о существовании других временных зонах кроме UTC: в коде жёстко прописано date_default_timezone_set('UTC');
Оставить комментарий
Сессии в PHP - вещь замечательная, она позволяет ускорить многие вещи на сайте, например, при выполнении долгих запросов к базе данных их можно записать в сессию и затем полученные значения сохранять в сессию и использовать по мере необходимости, однако, у сессий есть одно свойство, которое может гарантированно свести на нет все попытки увеличить быстродействие вашего сайта. Дело в том, что сессии в PHP блокируют ВСЕ страницы загружаемые для текущего пользователя до тех пор, пока страница, которая перавая успела заблокировть сессию не будет выгружена из памяти сервера. Т.е. если имеем одну медленную страницу с открытой сессией, которая выполняется, скажем, десять секунд - то в течении этих десяти секунд ВСЕ параллельно загружаемые странички будут блокированы и будут ждать пока медленная страница не будет сгенерирована до конца и выгружена и памяти.
В качестве примера, создадим две странички:
slow.php
<?php
print 'START....';
sleep(10);
print 'DONE';
fast.php:
<?php
print 'START....';
print 'DONE';
как видим обе странички работают вместе просто прекрасно, пока одна страничка генерируется десять секунд вторая загружается почти мгновенно, теперь добавим в обе страницы работу с сессиям:
slow.php
<?php
session_start();
print 'START....';
if (!isset($_SESSION['counter'])){
$_SESSION['counter'] = 0;
}
else {
$_SESSION['counter']++;
}
sleep(10);
print 'DONE';
fast.php:
<?php
session_start();
print 'START....';
print isset($_SESSION['counter'])?$_SESSION['counter']:'none';
print 'DONE';
Все попались, теперь, пока медленная страничка не завершиться, быстрая страничка будет ее ждать, т.к. файл с сессией остался заблокирован медленной страницей.
Одним из вариантов для решения подобной проблемы можно вызвать session_write_close(); перед заведомо медленной процедурой. В этом случае сессия будет закрыта досрочно и блокировка с файла будет снята, правда изменять какие либо значения в сессионных переменных уже не получится.
Финальный вариант slow.php:
<?php
session_start();
print 'START....';
if (!isset($_SESSION['counter'])){
$_SESSION['counter'] = 0;
}
else {
$_SESSION['counter']++;
}
session_write_close();
sleep(10);
print 'DONE';
В большинстве случаев это должно помочь. К счастью, каждый пользователь имеет собственную сессию и других пользователей медленная страница блокировать не будет. Но все равно, при работе с сессиям нужно учитывать возможности блокировки и по возможности их избегать.
Upd: Кроме этого, если после долгой операции нужно еще что нибудь поменять можно перед долгой операцией сначала закрыть сессию при помощи session_write_close() а потом её снова открыть при помощи session_start(). Однако, делать это можно только если вы ничего ещё не начали выводить в поток, иначе будет известный Warning: Headers already sent....
Коментариев: 1
Потребовалось обработать логи в которых дата хранилась примерно в таком формате 2010-12-03T18:34:33-0500. К сожалению в PHP нет толковой функции для преобразования строкового представления даты в timestamp. Как показала практика strtodate совершенно не учитывает последние четыре знака в строке которые она просто отбрасывет.
Пришлось парсерить строчку ручками и вот что получилось:
function covertdate($date){
preg_match('/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})([-+])(\d{2})(\d{2})/', $date,$res);
list($d,$year,$month,$day,$hour,$minute,$second,$digit,$zonehours,$zoneminutes) = $res;
$offset = ((int)($digit."1"))*($zonehours*3600+$zoneminutes*60);
$timestamp = gmmktime($hour,$minute,$second,$month,$day,$year)-$offset;
return $timestamp;
}
Коментариев: 1
Иногда бывает необходимо в какой либо функции находить какую либо информацию, сохраненную в MySQL табличке по её ID. При этом количество элементов в табличке может быть небольшим, а вот количество вызовов такой функции, наоборот большим. Каждый раз вызывать "select ... from" будет расточительно, вставить справочную табличку через JOIN не всегда возможно. Было бы логично сохранить где-либо все значения справочной таблички а потом брать по ключу (табличка как я уже сказал небольшая). Самое лучшее решение, которое я придумал на сегодня — это воспользоваться static переменным (их можно применять не в только для синглитонов)
static function getDocumentTypeName($type_id){
static $cache;
if (!isset($cache)){
$cache = Db::selectAssoc("select type_id, type_name from doc_types");
}
return $cache[$type_id];
}
При этом переменныя $cache остается в области видимости самой функции, что очень удобно, заполняется она один единственный раз при первом вызове функции.
Оставить комментарий