Немного багов

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

OR

В PHP, как самом умном, результатом операторов && и || является TRUE/FALSE. Казалось бы, логично, что результатом логического оператора является логическое значение. Однако, другие языки развратили меня тем, что возвращают в этом случае значение одного из аргументом.

В итоге строка $x || $y || $z вместо целочисленного значения одной из переменных, вернула предательский TRUE. После нелёгких поисков пришлось одну строку заменять на кучу ветвлений.

MBString

MBString работает с UTF-8? Хуй там! То есть в наиболее распространённом случае с UTF-8, а в общем случае зависит от того откуда руки у админа растут. Может и с какой-нибудь японской кодировкой.

Поэтому совсем уж по-правильному (чтобы не надеяться на настройки) кодировку нужно указывать одним из аргументов: mb_strlen($str, "UTF-8").

Какой я молодец, что с самого начала вынес строковые функции в отдельный классег : )

Контексты, будь они неладны

function func()
{
	global $global_var;
	echo $global_var.'<br />';
}
 
$global_var = 0;
 
for ($i = 0; $i < 5; $i++) {
	$global_var = $i;
	func();
}

Глобальные переменные в общем случае зло, но здесь так надо, поверьте :). Всё работает как надо.

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

function includePage()
{
	// Вычисляем $filename
	// ...
	require($filename);
}

Подключаемый внутри функции includePage() сценарий, выполняется в контексте этой функции, а $global_var становится локальной переменной этого контекста. И хрен до неё теперь достучишься из func().

// Нужно объявлять переменную так:
global $global_var;
$global_var = 0;
// Или так:
$GLOBALS['global_var'] = 0;

Преобразования типов

/* Рисуем пагинатор */
 
$pageSize      = 5;  // Количество элементов на страницу 
$elementsCount = 12; // Общее количество элементов
 
$pageCount = (($elementsCount - 1) / $pageSize) + 1; // Общее количество страниц
 
/* Текущую страницу достаём из GET */
$currentPage = isset($_GET['page']) ? intval($_GET['page']) : 1;
 
/* Проверяем диапазон */
if ($currentPage < 1) {
	$currentPage = 1;
} elseif ($currentPage > $pageCount) {
	$currentPage = $pageCount;
}
 
/* Расчитывем значения для разделя LIMIT SQL-запроса */
$limitStart = ($currentPage - 1) * $pageSize;
$limitSize  = $pageSize;

12 элементов по 5 на страницу. Всего 3 страницы. На последней странице отображаются последние два элемента выбираемые запросом с LIMIT 10, 5. Если запросить 4-ю страницу, то должна отобразиться 3-я. Запрашиваем 4-ю: отображается только 1 элемент.

Почему? Потому что $currentPage у нас не 3, а 3.2. Потому что нужно приводить типы: $pageCount = intval(($elementsCount - 1) / $pageSize) + 1;.

На поиск ошибок дошкольного уровня всегда уходит до черта времени.

Ух, собака

Решил как самый умный писать ваще-ваще правильно. До того, что все WARNING’и должны бросать исключение.

Решается это достаточно просто с помощью set_error_hanlder.

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

Оказывается у меня есть библиотека для работы с БД, где встречаются подобные вещи:

@parent::__construct($host, $username, $passwd, $dbname, $port, $socket);
if (mysqli_connect_errno()) {
    throw new goDBExceptionConnect(mysqli_connect_error(), mysqli_connect_errno());
}

Я глушу собакой WARINING и корректно обрабатываю ошибку. А вот пыху на собаку оказывается наплевать, он всё равно вываливается в error_handler с генерацией исключения.

var

Немного из JS.

function f()
{
	for (i = 0; i < 10; i++) {
		// ...
	}
}
for (var i = 0; i < 3; i++) { // Выполняется один раз
	f();
}

Локальные переменные нужно объявлять через var, иначе они будут ссылаться на локальные переменные из объемлющих контекстов.

Сам всех этому учу, но в первый раз попался, когда не просто нету var, но ещё и функция вызывается в цикле.

Задачка для JS-гурманов :)

function f1(b)
{
	if (b) {
		function f2(a) 
		{
			alert(a);
			return true;
		}		
	} 
	f2(b);
	return true;
}
 
f1(true);
f1(false);

Почему IE и Opera показывают 1-2, а в FF ошибка?

13 комментариев »

  • Ух, накатал… Про первое помню, недавно было ;)

    adw0rd, 25.04.2009, 5:46

  • Ты откуда взялся? :)

    vasa_c, 25.04.2009, 11:33

  • Я бот )

    adw0rd, 25.04.2009, 20:53

  • я аццкий ЖС гурман!

    function f1(b)
    {
    if (b) {
    f2 = function (a)
    {
    alert(a);
    return true;
    }
    }
    f2(b);
    return true;
    }

    f1(true);
    f1(false);

    так надо, ибо в ифе она объявляется как временная функция по ходу. поэтмоу ндо сделатьэту функцию глобальной :)

    mbstring. а в чем проблема поменять дефолт? mb_internal_encoding(«UTF-8»);

    зрзвгву, 4.06.2009, 13:27

  • Нет, скорее всего, потому что по высоким стандартам ECMA статическое определение функции недопустимо в ветвлении. Поэтому (function f2(a) {…}) рассматривается FF просто как анонимная функция, которая ничему не присваивается и сразу же уходит в dev/null. «f2» же доступно только в теле функции.

    А IE с Оперой кладут на высокие стандарты.

    А хотя кто их всех знает. Может и не поэтому :)

    vasa_c, 4.06.2009, 16:10

  • >mbstring. а в чем проблема поменять дефолт? mb_internal_encoding(«UTF-8»);
    Либу писал, которую можно использовать в других местах. Нельзя глобальные настройки менять.

    vasa_c, 4.06.2009, 16:12

  • 1) Про контексты — полезно, спасибо. Тольк вот include_once может проинклюдиться несколько раз, если мы его из разных ф-ций вызываем? Можно проверить, но лень =). Описал бы результат этого в статье.

    2) По поводу «@parent::__construct($host, $username, $passwd, $dbname, $port, $socket);
    «:
    я тоже так делаю. Так как быть-то?

    3) расскажи плизз поподробнее про класс для строк, че умеет делать, сильно ли снижает производительность?

    Koc, 6.06.2009, 16:24

  • >Тольк вот include_once может проинклюдиться несколько раз, если мы его из разных ф-ций вызываем?
    Нет, не может.
    Говорят может, если прописывать пути к файлу по разному, но я не проверял и это не относится к тому, что она вызывается внутри функции.

    vasa_c, 6.06.2009, 20:09

  • >я тоже так делаю. Так как быть-то?
    Пока что пишу с WARNING’ами. Поиск решения отложил до лучших времён.

    vasa_c, 6.06.2009, 20:10

  • >расскажи плизз поподробнее про класс для строк, че умеет делать, сильно ли снижает производительность?
    Да ничего особенного, в большинстве своём просто обёртки, дабы избежать случаев подобных вышеописанному:

    public static function length(str) 
    {
        return mb_strlen(str, 'UTF-8');
    }
    

    vasa_c, 6.06.2009, 20:12

  • Спасибо за статью! Со многим сталкивался, но есть и неизведанное :)
    По поводу страниц — можно считать так:
    >>> $pageCount = ceil($elementsCount / $pageSize); // Общее количество страниц
    и не париться с типами данных :)

    kirik, 20.07.2009, 9:36

  • по поводу set_error_hanlder
    в функцию добавить

    if (!error_reporting()) return;

    Как бы хм :)

    >>mbstring. а в чем проблема поменять дефолт? mb_internal_encoding(«UTF-8″);
    >Либу писал, которую можно использовать в других местах. Нельзя глобальные настройки менять.
    Вообще можно восстанавливать, если уж либу, хотя смысла сейчас особо нету — utf8 уже везде…

    Ivan1986, 12.06.2011, 1:49

  • >if (!error_reporting()) return;
    Да, спасибо. Уже догадался с тех пор :)

    >utf8 уже везде…
    Да везде-то, везде. А потом хренась где-то и внезапно оказывается, что не везде.

    vasa_c, 12.06.2011, 9:22

Leave a comment