Archive for the 'PHP' Category

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

Результат команды

Обнаружил, что команды серии passthru, exec и system не всегда возвращают результаты работы выполненной команды, что есть просто жах.

Теперь я использую рукотворную команду, полностью удовлетворяющую эти мои потребности:

//execs shell command and returns all its output
function syscall($command)
{
if ($proc = popen(”($command)2>&1″,”r”))
{
while (!feof($proc)) $result .= fgets($proc, 1000);
pclose($proc);
return $result;
}
}

Пример использования:

$output = syscall(’pwd’);
echo htmlspecialchars($output);

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

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

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

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

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

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

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

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

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

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

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

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

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

Передача по ссылке

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

Все вы знаете, что параметры в функцию можно передавать

  1. По значению, например, function f1($p)
  2. По ссылке, например, function f2(&$p)

В первом случае в функцию передастся копия фактического параметра, т.е. по сути будет 2 копии одних данных. Во втором случае та же переменная передастся в функцию, и она будет работать с её данными напрямую.

Я подумал - а что, если стараться передавать в свои функции в PHP параметры по ссылке везде где можно - оно ж напрямую, быстрее…

Получилось наоборот.

Вот мои тестовые примеры:

function func($a)
{
return strlen($a);
}

function func2(&$a)
{
return strlen($a);
}

a=str_repeat(”1″, 100000);

При запуске теста, который заключался в вызове 50 000 раз этих функций и передаче им $a в качестве фактического параметра, нарисовались такие данные:

  1. What a hell??первая функция - 0.51 сек.
  2. вторая функция - 13.33 сек.

Получается, первый способ - это 3.9% от второго по скорости (в 91 раз быстрее).

И что, где правда?

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

Проблемы обновления сессии в 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);
Скакунов Александр

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

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

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

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

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

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

Жрёт ли PHP память

Спорим с коллегами по поводу того, как и сколько PHP жрёт памяти.

Две ветки спора:

  1. отличается ли подключение библиотек через include и require;
  2. съедается ли сразу память под все имеющиеся в библиотеках функции либо по вызову.

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

Повырубал ненужные библиотеки (почти все), и апач стал в памяти меньше
на 42 байта ;]

Поэтому придумал другой тест.

Как тестил:

- запускал апач и записывал кол-во занятой им памяти;
- обновлял страницу в броузере и в процессе снимал показания памяти;
- закрывал апач.

Тест №1.

<?
$var = str_repeat(”asdf”, 123456789);
?>

Было : 11 980
Пик  : 25 500
Стало: 13 436

Тест №2.

<?
function f1()
{
$var = str_repeat(”asdf”, 123456789);
}
?>

Было : 11 980
Пик  : 13 476
Стало: 13 476
Смею сделать вывод, что просто лежащие в библиотеках функции только “индексируются”, но память сразу под них не выделяется (косвенный признак: пока функцию не вызовешь - PHP не скажет, есть ли в ней ошибки).

А вы что скажете?

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

utf8 в PHP

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

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

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

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

Покупайте наши маны!

Читаю электронный мануал по экзамену по PHP, и уже в первой части нашёл багу: говорят о трёх тагах, а приводят четыре.

Ошибка в мануале

Хорошо, что я бумажный ман не купил.

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

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

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

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