Archive for the 'webdev' Category

Скакунов Александр

Мега-инструмент

Мега-инструментЯ открыл для себя PEAR

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

  • работа с базой данных;
  • кэширование страниц и их частей;
  • несколько шаблонных систем - теперь не надо отдельно подключать (и изучать) Smarty;
  • есть встроенный пакет для работы с AJAX - тоже теперь не придётся искать что-то на стороне;
  • даже есть пакет для работы с облаком тэгов (правда, пока бета).

К плюсам также отношу простоту установки, а также возможность подгрузки нужных пакетов (есть специальная тулза для этого).

Конечно, нет ничего идеального:

  • некоторые функции недо-документированы;
  • на хостинге, похоже, его придётся ставить отдельно;
  • и какие-то вещи кажутся неудобными из-за своей новизны для меня,

но в целом я очень доволен.

По поводу хостинга и “ставить” - я свои проекты держу на одном сервере, так что все они теперь просто будут обращаться к PEAR за нужными функциями, что мне видиться очень удобным нововведением: если раньше я вносил улучшение в какой-то общий плагин в одном проекте, то надо было обновлять его во всех остальных; теперь же все “плагины” будут в одном месте (это, конечно, не чистая заслуга ПЕАРа, а приятное дополнение).

В общем, централизация меня радует.

P.S. К минусам отношу также необходимость иметь PHP в виде exe-файла, что совсем не очевидно (просто ругается на неопределённую переменную окружения) и не совсем удобно. Ну да ладно, поставил и забыл. Надо в настройках переменных окружения (Win+Pause, вкладка “Дополнительно“, кнопка “Переменные среды̶ ;) добавить новую переменную PHP_PEAR_PHP_BIN с путём к php.exe (например, “F:\www\apache\php.exe“). Уф!

Скакунов Александр

Множественные события в JavaScript

JavaScriptПорой возникает необходимость выполнить одно и тоже действие по разным событиям. Например, нужно отследить изменения ввода текста в инпут и соответственно обновить другое поле.

Разумеется, надо поставить вызов функции слежения в событие onchange(). Единственная загвоздка в том, что оно вызывается только при потере объектом фокуса, а мне нужна интерактивность. ОК, добавляем тот же вызов в событие onkeyup(), которые будет выстреливать после отпускания любой клавиши. Вызов onchange() оставляем на месте - вдруг пользователь мышкой текст вставит, а к клавиатуре не притронется.

Вопрос в том, как избежать одинаковых вызовов сразу в обоих событиях:

<input type=”text” name=”link_user” onchange=”function_call()” onkeyup=”function_call()”/>

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

<input type=”text” name=”link_user” onchange=”function_call()” onkeyup=”this.onchange()“/>

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

Скакунов Александр

Проблемы обновления сессии в PHP

Tuning the session...Столкнулся с такой проблемой: юзера в моей веб-системе логиняться с использованием сессий; время жизни сессии я выставил 1 неделю; при этом пользовательская сессия умирает действительно через неделю, но считая от первого логина, а не от последнего, т.е. не важно как часто юзер пользуется системой, время жизни его сессии не сдвигается, что для меня было не очевидно.

Раньше мой код выглядел так:

session_save_path(getcwd() . DIRECTORY_SEPARATOR . “temp”);
session_set_cookie_params(60*60*24*7); // 1 week
session_start();

Кстати, рекомендую сессии хранить где-то у себя в папке, а не в стандартном /tmp, который может быть доступен не только вам одному :] У меня тоже не идеальное решение, т.к. моя папка temp доступна через веб, но выключение показа её содержимое через Options -Index в .htaccess - вполне приемлимое решение для меня.

После небольшого RTFM код приобрёл такой вид:

session_save_path(getcwd().DIRECTORY_SEPARATOR . “temp”);
session_start();
setcookie(session_name(), session_id(), time()+60*60*24*7, “/”);

Т.о. каждое посещение моего ресурса обновляет время жизни сессии (expiration date), и самые активные юзера не будут испытывать трудностей с постоянно появляющейся формой логина.

[update] Комменты к посту (спасибо, парни!) и ман на php.net подсказывают, что надо прикрутить защиту от сборщика мусора. Перед session_start() надо поставить такую строку:

ini_set(’session.gc_maxlifetime’, 60*60*24*7); //1 week

P.S.  “Неделя” в примерах выше - абстракция. У меня другие значения, более актуальные для моей задачи. Попробуйте подставить свои значения - сначала с помощью опытных программистов, потом самостоятельно. Проявите фантазию!

Скакунов Александр

Долой колбасу!

Сидит у меня в РНР-коде такая “колбаса”, которая пытается выставить переменной $page значение - сначала из сессии, потом из хттп-переменной, ну и как дефолт единице:

Колбаса
 // Set page number
if (isset($_SESSION['page']))
  $page = $_SESSION['page'];
if (isset($attributes['page']))
  $page = $attributes['page'];
if (!isset($page) || $page == '')
  $page = 1;

Ничего не могу с собой поделать - мозолит глаза. Попробовал оптимизировать - код писал не я; подумал, как бы я сделал. Убрал последний предикат, дефолт поставил в начало, что логично.

// Set page number
$page = 1;
if (isset($_SESSION['page']))
  $page = $_SESSION['page'];
if (isset($attributes['page']))
  $page = $attributes['page'];

Всё равно колбаса, только короче. Ведь по сути выполняется почти одно и то же действие… Думай, Саша, думай. И придумал, вернее, вспомнил. В SQL такой код писать трудно, поэтому там есть функция COALESCE, которая возвращает значение того аргумента из переданных, который первым оказался не NULL:

SELECT COALESCE(NULL,1); – вернёт 1

Я решил применить тот же подход и для PHP. Вот что у меня получилось (я оставил оригинальную поддержку переменного числа аргументов):

//Returns the first non-empty value in the list
function coalesce()
{
  for($i=0; $i < func_num_args(); $i++)
  {
    $arg = func_get_arg($i);
    if(!empty($arg))
      return $arg;
  }
  return “”;
}

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

$page = coalesce($attributes['page'], $_SESSION['page'], 1); // Set page number

Хинт: в конец имеет смысл засунуть константу, чтобы переменная была проинициализированна в любом случае. Кстати, в ColdFusion есть удобный оператор, который одним вызовом реализует такой вот код:

if(!isset($var))
  $var = $default_value;

Теперь мы тоже так можем:

$var = coalesce($var, $default_value);
Скакунов Александр

defer

Озадачило меня вдруг, зачем хтмл-тэгу SCRIPT нужен атрибут DEFER ?

Первый же хит в гугле выдал такую инфу:

defer: Indicates that the script is not going to generate any document content. The browser can continue parsing and drawing the page.

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

Скакунов Александр

Обязательный checkbox

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

Есть одна проблема - переменная, которой соответствует инпут типа checkbox, создаётся только если этот чекбокс был включён (чекнут). Если он выключен, то переменная, соответственно, не создастся, и isset($checkbox_var) будет возвращать false.

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

Выход найден: перед тем, как выводить инпут чекбокса, надо вывести хидден-инпут с таким же именем и значением 0. Логика проста - если чекбокс выключен, то хидден-инпут создаст переменную с о значением 0; если же чекбокс включён, то он затрёт значение хидден-инпута единицей.

Скакунов Александр

utf8 в PHP

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

SELECT LOWER( 'вася ПУПКИН' )
UNION
SELECT LOWER( 'пупкин ВАСЯ' )
UNION
SELECT LOWER( 'пупкин ВАСИЛИЙ' )

Есть более человеческие варианты?

Скакунов Александр

ебандейское ООП в PHP

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

Вот блин, придётся вручную…

Скакунов Александр

ComboBox на HTML

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

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

ComboBox при вводе имени файла

Конечно, любой JavaScript-фреймворк имеет такой контрол, но все они какие-то недоделанные - раз, часто ведут себя по-разному в различных броузерах - два, не пашут без включённого JavaScript - три.

Тем не менее, самый, на мой взгляд близкий к виндовому вариант (что означает привычность использования - один из постулатов юзабилити) находится здесь, но в ИЕ немного колбасит низлежащий текст. Что поразило - ну очень похоже на то, как оно должно быть, я даже полез смотреть код, чтобы убедиться, что это на JS сделано, а не впаяли как-то новый контрол прямо в броузер :)

Знает кто действительно классную реализацию, чтобы вообще без нареканий?