Мои любимые ошибки в программировании

Original author: Paul Tero
  • Translation
За мою карьеру программиста я сделал огромное количество ошибок в нескольких различных языках. На самом деле, если я пишу 10 или больше строчек кода, которые работают с первого раза, я становлюсь подозрительным и принимаюсь тестировать его более тщательно, чем обычно, предполагая найти ошибку в синтаксисе, или неверную ссылку на массив, или неправильно записанную переменную, или что-то ещё.

Мне нравится подразделять эти ошибки на три большие группы: провалы, погрешности и недочеты. Провал – это когда ты сидишь тупо смотришь на экран и тихо говоришь «ой»; вещи вроде удаления базы данных или целого сайта, записи чего-либо поверх результата трехдневной работы, или случайной отсылки письма 20 тысячам человек.

Погрешности могут быть различными: от простых синтаксических ошибок (например, забыть поставить } ) до критических ошибок и ошибок в вычислениях.

Когда ошибка настолько неочевидна и неуловима, что это почти прекрасно, я зову это недочетом. Такое случается, когда кусок кода сталкивается с совершенно непредсказуемыми и весьма маловероятными обстоятельствами. Вы откидываетесь на спинку стула и думаете «Ого!», словно увидев яркую радугу или падающую звезду.

Невыключенный режим отладки

Первые две ошибки, о которых я упомяну в этой статье, были полноценными провалами.

Когда я впервые занялся фрилансом, я написал ряд PHP-библиотек для обработки запросов баз данных, форм и шаблонов страниц. Режим отладки был встроен в библиотеки на довольно глубоком уровне, который зависел от глобальной переменной $DEBUG.

Также я сохранял локальную копию каждого крупного сайта, над которым я работал, для разработки, отладки и тестирования. Таким образом, когда возникала проблема, я всегда мог установить $DEBUG=1; который сообщал мне различные вещи, например, все выполняемые операторы базы данных. Я редко использовал этот метод поиска ошибок для онлайн-сайтов, он был предназначен для локального использования.

Но однажды я работал поздно ночью, исправляя небольшую проблему на одном популярном сайте, занимающемся электронной коммерцией. Я поставил $DEBUG=1; вверху нескольких страниц и переключался между ними. От усталости в голове все смешалось, и в конце концов я каким-то образом добавил переменную отладки к самой важной странице сайта, которую пользователи видят сразу после нажатия кнопки «Оплатить сейчас», и в такой виде загрузил на рабочую версию сайта.

На следующее утро я рано ушел из дому, и, вернувшись в 9 вечера, обнаружил на автоответчике 12 сообщений, одно другого раздраженнее, и гораздо больше и-мейлов. В течение примерно 20 часов пользователи, нажимавшие на «Оплатить сейчас», видели приблизительно следующее:
image
Я потратил всего 10 секунд на исправление ошибки, но куда больше времени ушло на извинения перед клиентом за целый день упущенных заказов.

Усвоенные уроки

Я поразмыслил над этим случаем и установил, что следует:
1. Избегать работать поздно ночью
2. Проводить полное тестирование каждый раз, когда я делаю даже незначительные изменения в обработке заказа
3. Убедиться, что отчеты об отладке никогда не появятся на работающем сайте
4. Снабдить клиента контактными данными для чрезвычайных обстоятельств.

Внимательная отладка

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

function CanDebug() {
 global $DEBUG;
 $allowed = array ('127.0.0.1', '81.1.1.1');
 if (in_array ($_SERVER['REMOTE_ADDR'], $allowed)) return $DEBUG;
 else return 0;
}
function Debug ($message) {
  if (!CanDebug()) return;
  echo '<div style="background:yellow; color:black; border: 1px solid black;';
  echo 'padding: 5px; margin: 5px; white-space: pre;">';
  if (is_string ($message)) echo $message;
  else var_dump ($message);
  echo '</div>';
}

Массив $allowed содержит мой IP адрес для локального тестирования (127.0.0.1) и внешний IP.
Теперь я могу выводить вещи вроде:

$DEBUG = 1;
Debug ("The total is now $total"); //about a debugging message
Debug ($somevariable); //output a variable
Debug ("About to run: $query"); //before running any database query
mysql_query ($query);

И быть увереным, что никто кроме меня не увидит никаких сообщений об отладке. При условии, что вышеупомянутые переменные указаны, код будет выглядеть так:
image
Для большей безопасности я также мог перенести сообщения об ошибках внутрь HTML-комментариев, но тогда мне бы пришлось долго копаться в коде, чтобы найти нужный кусок.
У меня есть другой полезный фрагмент кода, который можно поставить в верхней части страницы или конфигурационного файла, чтобы все PHP уведомления, предупреждения и ошибки были видны только мне. Ошибки и предупреждения будут записаны в логах, но не показаны на экране.

if (CanDebug()) {ini_set ('display_errors', 1); error_reporting (E_ALL);}
else {ini_set ('display_errors', 0); error_reporting (E_ALL & ~E_NOTICE);}

Дебаггеры

Подобный метод удобен для быстрого нахождения ошибок в строго определенных фрагментах кода. Также существуют различные инструменты для отладки, такие как FirePHP and Xdebug, способные дать огромное количество информации о коде. Они также могут действовать невидимо, выводя список всех вызовов функций в лог-файл, без показа пользователю. Xdebug может быть использован так:

ini_set ('xdebug.collect_params', 1);
xdebug_start_trace ('/tmp/mytrace');
echo substr ("This will be traced", 0, 10);
xdebug_stop_trace();

Этот код регистрирует все вызовы функций и их параметры в файле /tmp/mytrace.xt, который выглядит следующим образом:
image
Xdebug также показывает гораздо больше информации о любом PHP предупреждении или ошибке. Однако его необходимо устанавливать на сервере, так что это скорее всего неосуществимо для большинства хостингов.
FirePHP, с другой стороны, работает как PHP-библиотека, взаимодействующая с аддоном Firebug. Вы можете выводить информацию о дебаггинге из PHP прямо в консоль Firebug  — опять-таки невидимо для пользователя.
В обоих методах функция типа вышеуказанной CanDebug все так же полезна для того, чтобы трассировки стека и создание лог-файлов не были доступны каждому обладателю Firebug.

Выключение режима отладки

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

Несколько лет назад меня попросили создать массовый скрипт для электронной почты для рассылки ежедневных e-мейлов более чем 20 тысячам подписчиков. Во время разработки я использовал что-то похожее на функцию CanDebug, чтобы иметь возможность протестировать скрипт без отсылки письма. Функция отсылки и-мейла выглядела примерно так:

function SendEmail ($to, $from, $subject, $message) {
  if (CanDebug() >= 10) Debug ("Would have emailed $to:\n$message");
  else {
    if (CanDebug()) {$subject = "Test to $to: $subject"; $to = "test@test.com";}
    mail ($to, $subject, $message, "From: $from");
  }
}

Если я ставил $DEBUG=1, скрипт посылал е-мейлы (все 20 тысяч) на тестовый адрес, который я мог проверить. Если я ставил $DEBUG=10, он сообщал мне, что пытается отослать е-мейл, но на самом деле ничего не отсылал. Вскоре после запуска со скриптом начались проблемы. Я думаю, у него закончилась память из-за малопроизводительной обработки информации 20000 раз подряд. В какой-то момент я углубился в исправление какой-то ошибки, забыл о своей переменной $DEBUG (или же мой внешний IP не вовремя изменился) и случайно отправил письма 20 тысячам человек.

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

Усвоенные уроки

Я очень порадовался тому, что просто оставил слово “test” в качестве темы и содержания письма, а не какое-нибудь высказывание, отражающее мое недовольство возникшим багом. Я усвоил, что надо:
1. Быть особенно осторожным, когда тестируешь массовые скрипты для электронной почты, — проверять, работает ли режим отладки.
2. Посылать тестовые и-мейлы как можно меньшему количеству людей.
3. Всегда писать что-либо вежливое в тексте письма, например, «Пожалуйста, проигнорируйте это тестовое сообщение». Нежелательно писать что-то вроде «Мой клиент — дурень» — мало ли это прочитают 20 тысяч ничего не подозревающих инвесторов.

Пустая страница PHP

Теперь мы добрались из царства провалов до области малозаметных ошибок. Если вы хотите увидеть ошибку, от которой трудно избавиться, похороните нижеследующее где-то в глубинах своего кода:

function TestMe() {TestMe();}
TestMe();

В зависимости от браузера и версий Apache и PHP на сервере вы можете получить пустую страницу, “This Web page is not available,” критическую ошибку, связанную с недостатком памяти, или предложение «Сохранить» или «Открыть» страницу:
image
Это по существу вызывает бесконечную рекурсию, которая может стать причиной недостатка памяти и/или прекращения работы серверного потока. Если он прекращает работать, в логах ошибок может остаться небольшой след:
[Mon Jun 06 18:24:10 2011] [notice] child pid 7192
exit signal Segmentation fault (11)

Это дает нам указание на то, где и почему произошла ошибка. И все быстрые методы дебаггинга с добавлением строк вывода в разных местах не дадут особого результата, поскольку пока проблемный код приводится в исполнение, целая страница не будет работать. В основном, это происходит из-за того, что PHP посылает браузеру сгенерированный HTML лишь периодически. Поэтому добавление множества выражений flush(); по крайней мере покажет вам, что ваш скрипт делал непосредственно перед рекурсивной ошибкой.
Конечно, код, вызывающий эту ошибку, может быть куда изощренней, чем показанный выше. Он может включать методы, вызывающие классы в других классах, которые отсылают обратно к изначальным классам. И эта ошибка может происходит лишь в трудновоспроизводимых обстоятельствах, и только потому что вы изменили что-то еще где-либо еще.

Усвоенные уроки

1. Знать расположения логов ошибок на случай, если там что-нибудь будет записано.
2. В подобных ситуациях stack-tracing дебаггеры вроде Xdebug могут быть очень полезны.
3. В противном случае, приберегите кучу времени, чтобы пройти весь код, строку за строкой, и закомментировать ненужные части, пока он не заработает.

Неправильный тип переменной

Эта ошибка часто случается в базах данных. Если даны эти SQL операторы…

CREATE TABLE products (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(60),
  category VARCHAR(10),
  price DECIMAL(6,2)
);
INSERT INTO products VALUES (1, 'Great Expectations', 'book', 12.99);
INSERT INTO products VALUES (2, 'Meagre Expectations', 'cd', 2.50);
INSERT INTO products VALUES (3, 'Flared corduroys', 'retro clothing', 25);

… угадайте, что будет возвращаться, если вы запустите следующее?

SELECT * FROM products WHERE category='retro clothing';

Ответ – ничего, поскольку колонка категории длиной всего в 10 символов, так что категория последнего продукта урезана до retro clot. Неожиданное исчезновение недавно измененных продуктов или новых элементов меню может внести большую неразбериху. Но обычно это довольно легко исправить:

ALTER TABLE products MODIFY category VARCHAR(30);
UPDATE products SET category='retro clothing' WHERE category='retro clot';

image
Я сделал более серьезную ошибку при работе над своим первым крупным сайтом электронной коммерции. В конце процесса заказа сайт просил клиента ввести реквизиты кредитной карты и затем вызывал Java-программу, которая посылала запрос о платеже в систему Barclays ePDQ. Сумма исчислялась в пенсах. Я не был хорошо знаком с Явой, поэтому взял за основу найденный образец кода, который представлял сумму как
short total;
Java-программа вызывалась из командной строки. Если она не возвращала ничего, то транзакция считалась выполненной, клиент получал и-мейл и заказ был выполнен. Если при проверке кредитной карты возникала ошибка, программа возвращала сообщения типа «Карта не авторизована» или «Карта не прошла проверку на подлинность».
Короткие целые числа могут хранить в себе значения между -32768 и +32767. Эти числа казались огромными. Но я не обратил внимания на то, что сумма исчислялась в пенсах, а не фунтах, то есть, наибольшей возможной суммой было £327.67. И самое худшее, что если сумма заказа была больше этой, Java-программа просто прекращала работу и не возвращала ничего. Это выглядело точно так же, как и удачно завершенный заказ, и далее процесс покупки шел как обычно. Ошибка была замечена то ли бухгалтерией, то ли бдительным и честным покупателем только через несколько месяцев, после нескольких больших неоплаченных заказов.

Усвоенные уроки

1. Назначая тип колонке в базе данных или переменной, надо быть предусмотрительным.
2. Надо убедиться, что успешное завершение программы отличается от программы, перестающей работать.

«Ошибки одного пенса»

Среди моих любимых ошибок можно назвать те, что вызывают расхождение всего лишь в 1 пенс (цент, эре или другую монету). Они мне нравятся тем, что их очень сложно отследить, и они часто сводятся к ошибке в округлении.
Несколько лет назад мне нужно было создать для одного сайта быструю функцию в JavaScript, выводящую количество денег. Я использовал это:

<script type="text/javascript">
function GetMoney (amount) {return Math.round (amount * 100) / 100;}
</script>

Однако вскоре выяснилось, что такие суммы как 1.20 выводились на экран в виде 1.2, что выглядело непрофессионально. Поэтому я поменял код таким образом:
<script type="text/javascript">
function GetMoney (amount) {
  var pounds = Math.floor (amount);
  var pence = Math.round (amount * 100) % 100;
  return pounds + '.' + (pence < 10 ? '0' : '') + pence;
}
</script>

Главное различие — лишний 0 в последней строке. Но так как теперь пенс вычисляется отдельно, оператор % нужен, чтобы получить остаток, когда количество делится на 100. Попробуйте найти такие маловероятные обстоятельства, при которых этот код вызовет ошибку.
Это случилось на сайте, где продавали бисер. Тогда я узнал, что бисер может продаваться в различных формах и количествах, включая изготовленные на заказ смеси, содержащие дробные значения. Однажды клиент купил 1.01 предмета, стоившего £4.95, и заплатил всего £4.00. Поскольку сумма была определена как 4.9995, программа округлила пенсы до 100, а % 100 оставило 0 пенсов. Таким образом, заплаченная сумма снизилась до 4 фунтов.
image
Простой недочет в округлении, где за 101 бусину, проданную по £4.95 за сотню, заплатили £4 вместо £5.
Я быстро поправил код:

<script type="text/javascript">
function GetMoney (amount) {
  var pounds = Math.floor (amount);
  var pence = Math.floor (amount * 100) % 100;
  return pounds + '.' + (pence < 10 ? '0' : '') + pence;
}
</script>

Впрочем, это не было удачным исправлением, поскольку оно округляло £4.9995 до £4.99, что мешало синхронизации с любыми соответствующими вычислениями со стороны сервера. Еще хуже было то, что в случае заказа 0.7 чего-либо, стоившего £1.00, сумма выходила 69 пенсов вместо 70! Это происходило потому что такие числа с плавающей запятой, как 0.7 представляются в бинарном коде скорее как 0.6999999999999999, что затем будет уменьшено до 69 пенсов вместо округления до 70.
Это настоящая «однопенсовая ошибка». Чтобы исправить ее, я добавил еще одно округление в начале:

<script type="text/javascript">
function GetMoney (amount) {
  var pence = Math.round (100 * amount);
  var pounds = Math.floor (pence / 100);
  pence %= 100;
  return pound + '.' + (pence < 10 ? '0' : '') + pence;
}
</script>

Теперь у меня было четыре строчки весьма сложного кода, чтобы сделать одну очень простую вещь. Сегодня, пока я писал эту статью, я обнаружил встроенную в Javascript функцию, которая справится со всем этим:

<script type="text/javascript">
function GetMoney (amount) {return amount.toFixed (2);}
alert (GetMoney (4.9995) + ' ' + GetMoney (0.1 * 0.7));
</script>


Предоставление скидки с PayPal

PayPal – это «однопенсовая ошибка», которая ждет своего времени. Многие сайты предлагают коды, которые дают скидку в некоторое количество процентов от суммы заказа. Она высчитывается в самом конце. Если вы заказали 2 предмета по 95 пенсов, общая сумма будет £1.90, и вы получите скидку в 19 пенсов, следовательно, заплатите £1.71.
Однако PayPal не поддерживает такой тип скидок. Если вы хотите, чтобы PayPal показывал предметы в вашей покупательской корзине, вам надо отдельно посчитать цену и количество каждого из них:

<input name="item_name_1" type="hidden" value="My Difficult Product" />
<input name="amount_1" type="hidden" value="0.99" />
<input name="quantity_1" type="hidden" value="1" />

Таким образом, вы должны получить скидку за каждый предмет отдельно. 10% скидки от 95 пенсов оставляет 85.5 пенсов. PayPal не оперирует дробными числами, поэтому вам надо округлить их до 86 пенсов, что дает общую сумму £1.72 в PayPal. Если округлять до 85p, общая сумма будет £1.70.
Чтобы решить эту проблему, мне также пришлось высчитывать скидку для каждого предмета в отдельности. Вместо обычного расчета 10% × £1.90, код аккумулирует скидку от предмета к предмету, каждый раз используя всю сумму пенсов. При условии, что $items это PHP массив предметов заказа:

$discount = 0; $discountpercent = 10;
foreach ($items as $item) {
 $mydiscount = floor ($item->price * $discountpercent) / 100;
 $item->priceforpaypal = $item->price - $mydiscount;
 $discount += $mydiscount * $item->quantity;
}

Усвоенные уроки

1. Не изобретайте колесо, даже очень маленькие колеса, которые выглядят просто.
2. Если у вас появляется расхождение в 1 пенс, проверьте, где и как цифры округляются.
3. Избегайте представления цен с помощью переменных формата float, если это возможно. Вместо этого для пенсов и центов используйте целые числа, а в базах данных используйте тип переменных с фиксированной запятой — DECIMAL.
Перевод часов

Я бы не назвал эту ошибку «погрешностью». Она появляется при особых редких обстоятельствах, поэтому с точки зрения программиста это скорее «недочёт». Она выходят за границы всего, что программист в состоянии предусмотреть.
Можете ли вы угадать, что не так с этой вроде бы безобидной строкой кода, которая выделяет заказы, выполненные более одной недели назад?

mysql_query ("SELECT * FROM orders WHERE completeddate < '" .
  date ('Y-m-d H:i:s', (time() - 7 * 86400 + 600)) . "'")

Я использовал похожую строку в системе для повторяющегося еженедельного заказа. Она выбирала заказы, выполненные на прошлой неделе, дублировала их и оформляла их для текущей недели. 86,400 – количество секунд в одном дне, так что time() — 7 * 86400 было ровно неделю назад, а +600 добавляет 10 минут запасного времени.
Это был малобюджетный метод реализации повторяющихся заказов. Если бы у меня было больше времени, я бы создал отдельную таблицу и/или покупательскую корзину для разграничения повторяющихся и неповторяющихся заказов. Получилось так, что этот код работал хорошо в течение нескольких месяцев и по загадочным причинам вышел из строя в конце марта.
Устранение недочета и его последствий заняло кучу времени, пришлось выполнять заказы вручную. Еще больше времени ушло на выявление причины, особенно из-за того, что я должен был заставить целый сайт считать, что на дворе другой день.
Я, в общем-то, уже проговорился о причине в названии раздела: я забыл взять в расчет перевод часов на летнее время, когда одна неделя меньше, чем 7*86400 seconds.
Сравните следующие три способа получения даты ровно недельной давности. Последний – самый элегантный. Я лишь недавно обнаружил его:

$time = strtotime ('28 March 2011 00:01');
echo date ('Y-m-d H:i:s', ($time - 7 * 86400)) . '<br/>';
echo date ('Y-m-d H:i:s', mktime (date ('H', $time), date ('i', $time), 0,
  date ('n', $time), date ('j', $time) - 7, date ('Y', $time)));
echo date ('Y-m-d H:i:s', (strtotime ('-1 week', $time))) . '<br/>';

Усвоенные уроки

Из подобной ошибки трудно сделать общие выводы, но определенный урок был усвоен:
1. На сайтах с чем-либо повторяющимся не забывайте принимать во внимание часовые пояса и перевод часов.
2. Опять-таки, не изобретайте колесо.

Заключение

Ошибки в программировании бывают разных форм и размеров. В данной статье они колеблются от вполне очевидных провалов до невероятно тонких просчетов. И, похоже, что все они подтверждают закон Мёрфи: если неприятность может случиться, она обязательно случится.

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

Кроме того, ошибки чаще встречаются на популярных сайтах – в основном потому, что они делаются множеством людей, но также потому, что исправление одной ошибки может повлечь за собой появление новых в других местах. А значит, следует думать наперед и внимательно исправлять ошибки.
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 108

    +18
    Половины указанных ошибок можно было бы избежать, используя готовые проверенные решения.
      +2
      Да, главный полученный урок: «не изобретайте колесо».
        +9
        Ага, и не научиться ничему.
          0
          Ну одно дело пробовать, изучать, мастерить, другое дело — продакшн клиента. Есть разница. Не, я все понимаю, это палка о двух концах, спорить не буду.
        –37
        В статье с таким названием ожидал увидеть нечто действительно стоящее, а увидел PHP
          –2
          наиболее популярный язык для веб-разработки это уже не стояшее?: )
            +30
            Вы исходите из предпосылки, что массовость определяет полезность. Продолжая вашу логику: каждый житель планеты Земля производит дефекацию минимум раз в день. Вывод: говно популярнее пхп. Вывод: даже говно лучше, чем пхп. (кстати, в этом выводе что-то есть)
              +2
              есть разница между необходимыми функциями организма и выбором.
                –5
                Хорошо, вот вам другая аналогия: дошираком питается больше людей, чем пишет на пхп (хотя эти множества и пересекается). Вывод: доширак лучше похапэ. Доширак — говно. Вывод: говно лучше похапэ.
                  –1
                  вы опять же сравниваете котов со слонами
                  попробуйте провести аналогию с языками для веб-разработки
                    –13
                    С чего это? Нищеёбствовать и питаться дошираком — выбор. Нищеёбствовать интеллектуально и писать на похапэ — тоже выбор. Конечно, люди любят оправдывать свои выборы («мне платят за похапэ», «мне достаточно похапэ», «мне нравится вкус доширака», «а чо — дёшево и норм, под пивасик особенно»), но это не отрицает возможности строить по выборам людей суждение о них.
                      0
                      питаться дошираком не выбор, а суровая необходимость, вызванная нехваткой денег.
                        –3
                        что за бред
                          –11
                          Точно в той же мере писать на похапэ не выбор, а суровая необходимость, вызванная недостатком интеллекта и образования. Вы в точности уловили мою позицию.
                            +11
                            Не место красит человека, а человек место.

                            Так же и с языками.

                            А глупость про недостаток интеллекта и образования, хабраюзеры вам не простят и правильно сделают. ЧСВ надо лечить.
                            0
                            … а быдлокодить «программить» на PHP — необходимость, вызванная отсутствием чего?
                              0
                              УПС! Мой фэйл. уже ответили также. Заминусованный коммент не заметил. Мои два пойдут туда же в виду массовости оппозиции.
                        +24
                        А вот вам правильная аналогия с дошираком:
                        1) Доширак легко купить и перевозить с собой. Гораздо проще, чем кусок отбивной + овощи, которые перед употреблением надо еще и пожарить и сварить. Да и контейнер нужно купить для перевозки.
                        2) Стоит не дорого, и способен удовлетворить голод, а при прочих равных еще и хранится дольше.
                        3) Если есть в арсенале овощи, мясо, — можно выкинуть из пакетика приправы и глутамат натрия и сварить вполне себе диетический и полезный суп.
                        4) Среди дошираков есть вполне себе дорогие и более-менее полезные наборы, так же как среди мяса баранины может попасться крыса или дохлая кошка. Это как выбирать и где и почём покупать ;)
                        5) Есть овер 9000 рецептов прикольных и полезных блюд из яичной лапши — (а это основа дошираков). Огромный плюс в простоте приготовления элементарного и в наличии возможностей приготовить изысканное блюдо, добавив нужные ингредиенты и навыки.

                        Так что ваша аналогия откуда то из детских холиваров уровня «я на третьем курсе учуся, мне преподаватель сказал, что ПХП — говно, и ваще там нет типизации и т.д. и т.п.».

                        Ой, фейсбук там с ПХП мучается, ой ой ой, какая печалька… Интересно с чем другим он бы смог так быстро стартануть, вполне себе сносно работать и развиваться.
                          –17
                          Продолжайте питаться говномдошираком, я же не мешаю. На улице, разве что, постараюсь обойти стороной, чтобы не так вонь в нос била, но в целом это ваше право ;)
                            –1
                            >> фейсбук там с ПХП мучается, ой ой ой, какая печалька…

                            Да вроде ж FB с РНР уже давно отмучался, оставив на нём совсем маленький кусок по историческим причинам.
                              +3
                              Если за год ничего не поменялось, то «Facebook по-прежнему использует PHP, но перед исполнением скрипты компилируются в родной код процессора, ускоряя таким образом работу».

                              Источник: habrahabr.ru/blogs/personal/100020/
                                –4
                                Ну да. В статье, на которую вы ссылаетесь есть подтверждение моим словам: всё работает на С++ а РНР остался для парсинга страниц вроде.

                                Так-же и с mysql — он какбэ есть, но на практике это key-value хранилище, запросы ограничиваются словсми select, insert, update и where.
                                  +3
                                  Если верить поиску, C++ упоминается в статье два раза:

                                  1:
                                  «HipHop конвертирует скрипты на PHP в исходные коды на C++, которые затем компилируются для обеспечения хорошей производительности.»

                                  2:
                                  «PHP используется как фронт-энд, Erlang для чата, Ява и C++ тоже не остались без дела.»

                                  Не вижу здесь подтверждения ваших слов. Да, php компилируется в C++, но, насколько я понял, программисты пишут на php, C++ используется только для компиляции.

                                  Да и в тексте есть такие фразы:
                                  «PHP в Facebook используется практически повсеместно.»

                                  Как вы подметили, автор похоже слабо понимает разницу между key-value хранилищем и даже, ограниченной простыми запросами, реляционной базой данных.
                            +2
                            Уже второй комментарий подряд вы пишете про говно. Вывод: все ваши комментарии — говно.
                            Так?
                              –5
                              Отличная аналогия.
                              Уже второй день подряд вы посещаете туалет. Вывод: вы — говно. Так?
                          0
                          Расскажи об этом Цукербергу
                            +12
                            Вывод: дефекация важнее пхп, все правильно. Или вы скорее предпочтете проблемы с дефекацией, чем с пхп?
                          0
                          Для таких как Вы, авторы топиков пишут теги.
                            +1
                            Говорить это 1-2 тысячам читателей, использующим PHP — тоже в своём роде ошибка. Как вторая, описанная в статье, про рассылку e-mail. И, наверное, Вы не прочитаали совет по этому поводу :)
                            3. Всегда писать что-либо вежливое в тексте письма, например, «Пожалуйста, проигнорируйте это тестовое сообщение». Нежелательно писать что-то вроде «Мой клиент — дурень» — мало ли это прочитают 20 тысяч ничего не подозревающих инвесторов.
                              0
                              Говорить это 1-2 тысячам читателей, использующим PHP — тоже в своём роде ошибка. Как вторая, описанная в статье, про рассылку e-mail. И, наверное, Вы не прочитаали совет по этому поводу :)
                              3. Всегда писать что-либо вежливое в тексте письма, например, «Пожалуйста, проигнорируйте это тестовое сообщение». Нежелательно писать что-то вроде «Мой клиент — дурень» — мало ли это прочитают 20 тысяч ничего не подозревающих инвесторов.
                                0
                                Сорри, дубль.
                                +16
                                Трололо, какая разница, на чём писать? Если у тебя руки из ануса — не поможет даже твой любимый язык программирования.
                                  –20
                                  По количеству заработанных минусов можно судить о сплоченности PHP-разработчиков. Браво! Не каждое сообщество умеет день ото дня употреблять дерьмо и радоваться этому, а также отстаивать честь тех, кто наслаждается вместе с ними.
                                    +13
                                    По количеству минусов видно, что человек навязывающий свое мнение всем и каждому не пользуется уважением в обществе.
                                      –13
                                      Вы судите об уважении по количеству плюсов и минусов? А по карме, очевидно, определяете, с кем вам дружить.
                                        +1
                                        Когда я говорю об обществе — я имею множество пользователей ресурса — у всех этих пользователей есть право один раз проголосовать за любое высказывание любого другого. Все абсолютно в равных условиях. То что ваш комментарий ушел в минус говорит только о том, что большинство пользователей, которые сочли нужным проголосовать, считают ваше суждение неверным.
                                          –4
                                          Спасибо. И как же я сам не догадался.
                                          +4
                                          Причем я не думаю, что среди них все любители или программисты на PHP, какая-то часть просто переросла понятия типа «моя пиписька длинее чем твоя» и дают оценку конкретным результатам а не инструментальным средствам.
                                    +13
                                    Главный вывод: учить матчасть и думать перед тем, как что-то делаешь.
                                    Деньги и плавающая точка — зло.
                                      +2
                                      именно. это всё noobie-fails
                                      –1
                                      А я люблю ошибки под кодовым названием «Я все разрушил!»

                                      piccy.info/view3/1741264/b71ccb04afbb960ca21f4efbbe58046a/
                                        0
                                        Я поразмыслил над этим случаем и установил, что следует:
                                        1. Избегать работать поздно ночью
                                        Как я вас понимаю… У самого после ночных бдений порой случаются «провалы», но отучить себя от этого не выходит…
                                          +4
                                          Ночью вообще самое продуктивное время, никто не тревожит… коллеги, семья, клиенты…
                                            +2
                                            и на хабре почти никто не пишет, потому что все программят или спят.
                                              +2
                                              6 утра — самое продуктивное время. Проснулся свежим, голова варит на отлично. Никто не мешает. За окном рассвет.

                                              При этом, с 13 до 14 отличный дневной сон.
                                                +3
                                                Если живешь не один, заснуть в 10 вечера проблема.
                                                  +1
                                                  Ох, я думал я один такой. Тоже считаю, что работать утром — самое то. И при этом тоже днем тянет поспать. Но зато после дневного сна голова опять вежая и опять готов работать
                                                    0
                                                    :)
                                                0
                                                Это статья-перевод, так что это не к автору.
                                                +9
                                                Насчёт дат — нужно не изобретать колесо и использовать возможности базы.
                                                Для MySQL выборка за прошедшую неделю выглядет так: SELECT * FROM `table` WHERE `datetime_field` >= NOW() — INTERVAL 1 WEEK
                                                И.т.д., у MySQL весьма обширный список функций по работе с датами и такие вещи как летнее время уже обрабатываются за нас.
                                                  +1
                                                  BETWEEN кстати с датами тоже работает прозрачно — главное что бы тип столбца был date, time или datetime
                                                    +1
                                                    В связи с грядущим «недоперехором» на зимнее время 2011 нам еще долгое время придется отлавливать многие вещи, которые совсем не факт, что корректно обработаются за нас.
                                                      0
                                                      Поле типа datetime хранится с учётом текущей временной зоны — т.е. с текущей серверной часовой зоной. Если вам нужно показать пользователю другой временной зоны данные, то при установке соединения просто сделайте вот так:
                                                      SET time_zone = timezone;


                                                      Так что большинство проблем давно имеют решения, просто часто никто их не ищет.
                                                      0
                                                      Я после аналогичной ошибки с временем, перешел на ранение времени в GMT, но не отказался от секундного его представления.
                                                      –2
                                                      Действительно, зачем нам отладчик с брекпоинтами как у всех нормальных людей, мы лучше трейсов наделаем.
                                                        0
                                                        на сервере тоже отладчик?
                                                        0
                                                        Из одной крайности в другую. А почему бы не хранить и передавать данные в зашифрованном виде? (с ключами достаточной для будущих мощностей криптостойкости)
                                                          +1
                                                          Статья поучительная, хотя бы тем, что напоминает о некоторых действительно распространенных ошибках, большинство из которых совершаются из-за невнимательности и спешки.

                                                          Но перевод хромает… Местами очень напоминает гугл, подвергшийся рерайту. Например:

                                                          «Для третьего пункта я осуществил несколько функций, чтобы обеспечить показ сообщений о дебаггинге только мне самому».

                                                          Несколько не по-русски, хотя, конечно, смысл ясен. Тем не менее, рекомендую подправить текст и он будет читаться значительно легче.
                                                            0
                                                            переводил конечно же сам, но за замечание спасибо, подправил
                                                              +1
                                                              Да нет, ничего плохого нет в том, чтобы пользоваться переводчиками или, тем более, словарями. Но нужно стараться доносить мысль правильно на том языке, на который переводите. Старайтесь не допускать подстрочника.

                                                              Помню, нас учили стараться передать именно смысл, а не структуру предложения. Конечно, если ситуация не требует максимальной точности. Переведите предложение дословно, а потом напишите его так, будто вы излагаете заложенный в него смысл самостоятельно, не опираясь на текст оригинала. Я думаю, это даст хороший результат.
                                                            +2
                                                            я может чего не понимаю…
                                                            а почему для дебага нельзя использовать site.com/page.aspx?debug=1?
                                                              0
                                                              Лучше тогда уж в куки. Потому, что для POST-форм придётся каждый раз менять action, для ajax-запросов вообще какие-то хаки придумывать.
                                                              0
                                                              Было бы еще хорошо узнать, как работать с деньгами правильно.
                                                              Сейчас есть один проектик, в котором деньги приодят в рублях, переводятся в доллары и кладутся флоатом в базу…

                                                              Я теряюсь — что с этим делать?
                                                                0
                                                                ну в базе точно нужно использовать DECIMAL — вроде как специально для хранения денег придумали этот тип.
                                                                  0
                                                                  В mssql для этого есть типы decimal и money
                                                                    +2
                                                                    Лучше всего не работать с деньгами, если возникают такие вопросы.
                                                                      +4
                                                                      Если следовать такой логике — то вообще не стоит задавать вопросы, чтобы получать на них ответы.
                                                                      Жить в счастливом неведении. Что может быть лучше?
                                                                      0
                                                                      В банковских продуктах деньги хранятся в самых мелких единицах измерения, которые существуют в валюте. Например, тунисский динар состоит из 1000 милльем. Для каждой валюты составляется карта трансформаций, которая подготавливает данные для вывода. Карта — это набор коэффициентов, на которые умножается сумма, чтобы получить денежные единицы другого порядка
                                                                        0
                                                                        Откуда информация? Я думал что в банках понятие «валюта счета» отвечает в том числе и за то, в какой валюте хранятся данные. Ведь иначе тунисский доллар может вырасти или упасть?
                                                                          0
                                                                          Информация из содержимого таблиц, которые используются АБС. Валюта счета — информационное поле. Хоть и пишут USD, RUR, EUR, и так далее, реально деньги хранятся в центах, копейках и прочих мелких единицах.

                                                                          Простой пример. Человек взял кредит 1 тунисский динар. Заплатил обязательный платеж по телу кредита 1 милльем. Если вы храните в базе 1 динар, то вы сначала должны узнать из какого количества боллее мелких денег состоит динар, потому что вы должны вычитать не 0.01, а 0.001. Это на каждую операцию вы должны лезть в справочник и что-то там мудрить. Бездумная трата машинных ресурсов, не находите?

                                                                          Теперь вариант с хранением в милльемах. Человек взял в кредит 1000 милльем, заплатил 1, осталось заплатить 999. Такие операции как вычитание, сложение, умножение, деление, можно делать без справочников. А на момент отображения данных уже лезть в справочники и узнавать, какое количество динаров ему еще осталось платить.
                                                                            0
                                                                            А как насчет дробных частей? Наверняка есть какое-то устойчивое мнение, начиная с какого разряда можно отбрасывать цифры. В бухгалтерии до какого разряда идет расчёт процентов? И в какую сторону округляются суммы?
                                                                              0
                                                                              Поясняю еще раз. Данные хранятся в целочисленном виде. Не в float. Если попадается бухгалтер-задрот, который любит считать все до десятых копеек, то водите еще одну размерность — десятая копейки, тогда копейка будет 10 десятых копейки, рубль — 1000 десятых копеек. Все эти размерности будут играть роль на момент вывода единиц на экран пользователя, но ни в коем случае не на момент математических рассчетов.
                                                                        –2
                                                                        Храните в БД в flloat, при расчетах в php используйте только функции BCMath, дабы избежать неточностей в арифметических операциях с float'ами.
                                                                        +2
                                                                        Всегда писать что-либо вежливое в тексте письма, например, «Пожалуйста, проигнорируйте это тестовое сообщение». Нежелательно писать что-то вроде «Мой клиент — дурень» — мало ли это прочитают 20 тысяч ничего не подозревающих инвесторов.

                                                                        Взял за правило быть вежливым даже в комментариях. Их может никто и не видит, но мало ли…
                                                                          +2
                                                                          А может просто не стоит вести разработку и тестирование на продакшн сервере, не? Я с php не работаю, это там так принято разрабатывать/дебажить? белый список ip дебагеров очень мило :)
                                                                            0
                                                                            Вроде про разработку на продакшене нигде не было. Или я пропустила?
                                                                              0
                                                                              Пропустили, глава «Невыключенный режим отладки» :)
                                                                                0
                                                                                Там не про разработку на продакшене, а о том, что с продакшена загрузили на лив код с включенным режимом дебага.
                                                                              0
                                                                              а если ошибка вылезет как раз на продакшене
                                                                              +1
                                                                              стараюсь использовать 2 конфига (production и development) в которых храню все настройки
                                                                              и далее примерно так:
                                                                              switch ($_SERVER['SERVER_NAME']) {
                                                                              case 'www.site.com':
                                                                              case 'site.com':
                                                                              case 'aliasOfsite.com':
                                                                              define('MODE', "production");
                                                                              break;
                                                                              default:
                                                                              define('MODE', "development");
                                                                              break;
                                                                              }
                                                                                0
                                                                                Вот изменится у вас имя сервера, а обновить конфиг забудете…
                                                                                В условии по дефолту я бы предложил кидать ошибку, потому что запустить production конфигурацию локально тоже может быть нежелательно.
                                                                                  0
                                                                                  посмотрите как работает Zend Framework — там устанавливается переменная окружения и в зависимости от ее значения разбираются различные секции конфига
                                                                                • UFO just landed and posted this here
                                                                                    +3
                                                                                    Строил собаке будку, сделал огромное количество ошибок:
                                                                                    • сначала сделал крышу, в конце обнаружил, что она не подходит для будки
                                                                                    • собрав будку, понял, что забыл сделать дверь
                                                                                    • переделав будку понял, что она мала для моей собаки...


                                                                                    Выходит все строители г…
                                                                                    • UFO just landed and posted this here
                                                                                        0
                                                                                        Это не я ;)

                                                                                        Пробуйте сами:

                                                                                        function GetMoney (amount) {
                                                                                          var pounds = Math.floor (amount);
                                                                                          var pence = Math.floor (amount * 100) % 100;
                                                                                          return pounds + '.' + (pence < 10 ? '0' : '') + pence;
                                                                                        }
                                                                                        alert (GetMoney (4.9995) + ' ' + GetMoney (0.1 * 0.7));
                                                                                        
                                                                                        • UFO just landed and posted this here
                                                                                            –1
                                                                                            Кстати говоря, при работе с деньгами как раз таки округление чаще всего идет до меньшего целого, как ни странно. Ну а математическое округление пригодится именно в математических рассчетах.
                                                                                            • UFO just landed and posted this here
                                                                                                0
                                                                                                1) « 0.6999999999999999, что затем будет уменьшено до 69 пенсов вместо округления до 70» мы о них и говорим, вроде как
                                                                                                2) конечно, а до этого и нет необходимости округлять.
                                                                                                • UFO just landed and posted this here
                                                                                        0
                                                                                        > … угадайте, что будет возвращаться, если вы запустите следующее?
                                                                                        >
                                                                                        > SELECT * FROM products WHERE category='retro clothing';
                                                                                        >
                                                                                        > Ответ – ничего, поскольку колонка категории длиной всего в 10 символов, так что категория последнего продукта урезана до retro clot.

                                                                                        Если проверять warnings, то подобное можно заметить после первого же INSERT-а или хотя бы сразу понять почему SELECT возвращает неверный результат.

                                                                                        > Перевод часов
                                                                                        >
                                                                                        > Я бы не назвал эту ошибку «погрешностью». Она появляется при особых редких обстоятельствах,…

                                                                                        Вызвало улыбку, потому что по bogus-ам «перестали работать timestamp/даты в MySQL» я определяю приход весны и осени: редкие обстоятельства на миллионах инсталляций дают эффект =)

                                                                                        Статья в целом хорошая и полезная.
                                                                                          +2
                                                                                          Дебажу всегда в error_log с названием класса и номером строки, очень удобно
                                                                                          А после случая, когда админы напортачили и файл index.php отдался как текст, я стал писать в нём только одну строчку => require '../index.php' // который, естественно, не виден снаружи
                                                                                            0
                                                                                            А вот это действительно ценный совет. Как-то никогда не думал о такой ситуации, а ведь она более чем реальна!
                                                                                              +1
                                                                                              А что в индексе обычно такого важного?
                                                                                              Вот если бы был доступен конфиг — это да :)
                                                                                              0
                                                                                              3. Избегайте представления цен с помощью переменных формата float, если это возможно. Вместо этого для пенсов и центов используйте целые числа, а в базах данных используйте тип переменных с фиксированной запятой — DECIMAL.
                                                                                              Удивительно, но эта ошибка встречается очень часто (слишком часто!) и приводит к очень неприятным последствиям.
                                                                                              Добавлю, что в некоторых языках программирования и СУБД аналогичные типы могут называться Numeric, Currency или Money (а также smallmoney).

                                                                                              Я бы распечатал такой плакат и дал каждому разработчику, имеющему дело с денежными суммами.
                                                                                                0
                                                                                                Теперь у меня было четыре строчки весьма сложного кода, чтобы сделать одну очень простую вещь. Сегодня, пока я писал эту статью, я обнаружил встроенную в Javascript функцию, которая справится со всем этим:
                                                                                                <script type="text/javascript">
                                                                                                function GetMoney (amount) {return amount.toFixed (2);}
                                                                                                alert (GetMoney (4.9995) + ' ' + GetMoney (0.1 * 0.7));
                                                                                                </script>
                                                                                                Автор (оригинала) явно неполноценно тестировал то, что написал и не знает про баг с функцией toFixed() :)

                                                                                                Тестовый «эксплойт»:
                                                                                                alert([1.1255.toFixed(3), 0.1255.toFixed(3)]) // 1.125,0.126 - FF5, Chrome12, Opera 11.5
                                                                                                </script>
                                                                                                — на данный момент даёт ошибку в FF5, Chrome12, Opera 11.5. Не даёт ошибки в IE8, Safari5.

                                                                                                Или с использованием функции автора:
                                                                                                <script>
                                                                                                function GetMoney (amount) {return amount.toFixed(2);}
                                                                                                alert([GetMoney(1.155), GetMoney(0.155)]) // 1.16,0.15 - FF5, Chrome12
                                                                                                </script>

                                                                                                Такую ошибку даёт в FF5 и Хроме, но не факт, что Опера застрахована от ошибок для .toFixed(2).
                                                                                                  0
                                                                                                  Вот и баг для Оперы: alert([GetMoney(1.255), GetMoney(0.255)])
                                                                                                    0
                                                                                                    это больше похоже на округление к ближайшему чётному, а не на баг.
                                                                                                      0
                                                                                                      Ближайшее чётное — 6 (на 2-м месте после точки). 1.255 округляется до 1.26, а 0.255 округляется до 0.25, хотя должно тоже до 0.26 (и в IE8, Safari так и есть).
                                                                                                  0
                                                                                                  Так, чего-бы такого прикупить в Лондоне на £327.68, или больше. :)
                                                                                                    –4
                                                                                                    Большинство описанных ошибок лечатся грамотными тестами (которые лучше писать до реализации), а режим отладки лучше включать через переменные среды — тогда на целевой машине ничего ненужного выводиться не будет.

                                                                                                    И да, PHP таки говно. Любой современный язык (Python, Ruby) и Perl делают его как щенка по удобству пользования и выразительности синтаксиса. Я уж молчу про такие приятные штуки как CPAN — там есть практически все.
                                                                                                      0
                                                                                                      Вот же ж любовь компьютерщиков свести все к холивару :) Как удобство пользования и выразительность синтаксиса влияют на вышеуказанные ошибки??
                                                                                                        –2
                                                                                                        Чем более выразителен синтаксис языка, тем меньше кода надо писать, и тем меньше ошибок в итоге вылезет.

                                                                                                        Чем более удобен язык для решения задачи, тем меньше времени и усилий займет «натянуть» задачу на язык, и соответственно меньше ошибок будет совершено.

                                                                                                        Называть PHP удобным может только человек, который ничего больше не видел: в нем даже модулей до сих пор нет, я уж не говорю про активно внедряемые в другие языки функциональные фичи.
                                                                                                          0
                                                                                                          А ктоего таким называет?
                                                                                                          Просто в данном топике разбираются абсолютно другие ошибки. По-моему, они никак не связаны с удобством языка. Это более высокий уровень логики.
                                                                                                      0
                                                                                                      Насчет рассылок. Тут надежнее всего таки не позволить письму отправиться куда не надо.
                                                                                                      Для этого достаточно двух вещей:
                                                                                                      1. тестировать рассылки только на dev-сервере
                                                                                                      2. отправлять письма только по какому-то фильтру. У нас на работе, например, используется RoundCube с фильтром по нашим рабочим адресам. Остальные письма просто складываются на сервер и их можно просмотреть.
                                                                                                        +1
                                                                                                        по поводу отладки: я использую несколько расширенную схему, причём пришёл к ней почти сам даже не допустив косяка:

                                                                                                        define(DEVELOPER_IP, '212.121.212.121'); // assign office IP
                                                                                                        define(SHOWDEBUG, $_SERVER['REMOTE_ADDR'] == DEVELOPER_IP); // show DEBUG only for office IP

                                                                                                        // настройка ini-переменных
                                                                                                        ini_set('error_reporting', 'E_ALL & ~E_NOTICE');
                                                                                                        error_reporting(E_ALL & ~E_NOTICE);
                                                                                                        # /doc/root/../logs/errors.log, файл необходимо предварительно создать!
                                                                                                        ini_set('error_log', implode(DIRECTORY_SEPARATOR, array($_SERVER['DOCUMENT_ROOT'], '..', 'logs', 'errors.log')));
                                                                                                        if (SHOWDEBUG):
                                                                                                        // show errors and warnings
                                                                                                        ini_set('display_errors', 'On');
                                                                                                        ini_set('display_startup_errors', 'On');
                                                                                                        ini_set('log_errors', 'Off');
                                                                                                        ini_set('html_errors', 'On');
                                                                                                        ini_set('track_errors', 'On'); # If enabled, the last error message will always be present in the variable $php_errormsg
                                                                                                        else:
                                                                                                        // disable errors and warnings
                                                                                                        ini_set('display_errors', 'Off');
                                                                                                        ini_set('display_startup_errors', 'Off');
                                                                                                        ini_set('log_errors', 'On');
                                                                                                        ini_set('html_errors', 'Off');
                                                                                                        ini_set('ignore_repeated_errors', 'On');
                                                                                                        ini_set('ignore_repeated_source', 'On');
                                                                                                        ini_set('track_errors', 'Off'); # If enabled, the last error message will always be present in the variable $php_errormsg
                                                                                                        endif;
                                                                                                          0
                                                                                                          Да, с часами прикольно получилось, очень немногие знают все силу *nix'овой date.

                                                                                                          Only users with full accounts can post comments. Log in, please.