Показаны сообщения с ярлыком PHP. Показать все сообщения
Показаны сообщения с ярлыком PHP. Показать все сообщения

вторник, 1 марта 2011 г.

rlinkf

В рабочем порядке перенес мой маленький скрипт-редиректор rlinkf на другую платформу.
Ссылка (благодаря ему же) прежняя: http://zfilin.org.ua/link/rlinkf

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

суббота, 27 марта 2010 г.

rlinkf v1.0

Ура! Ура! Я закончил первую версию моего скрипта-редиректора и теперь я смогу быстро и просто делать всякие короткие ссылки вроде http://zfilin.org.ua/link/how-to-read (оригинальная ссылка: дли-и-и-и-и-и-инная ссылка)
Если вы хотите себе такое-же, то скрипт можно скачать по адресу http://zfilin.org.ua/link/rlinkf в разделе Downloads

четверг, 11 марта 2010 г.

Как сбросить HTTP-авторизацию (пример на PHP)

Иногда бывает необходимо защитить паролем одну страницу на сайте. Не всегда хочется использовать для этого сессии и прочие php заморочки. Для таких случаев удобно использовать HTTP-авторизацию.
Однако у нее есть один недостаток: нет нормального способа отлогиниться от страницы. Т.е. не закрывая браузер вы никак не сможете выйти из защищенной области так, чтобы при повторном входе страница снова потребовала бы у вас пароль.
На эту тему есть интересный трикс. Нужно снова зайти на страницу с заведомо не правильным логином и паролем. Сделать это можно, послав пользователя по ссылке вида:

http://user:pass@you-domain.com/you-page

Таким образом вы можете осуществить сброс HTTP авторизации. Конечно user и pass не должны быть зарегистрированы в системе.

Вот вам пример защищенной страницы на PHP:
<?php
 $admin_user='admin';
 $admin_password='12345';

 $request_path=parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

 if(isset($_GET['logout'])) {
  header("Location: http://x:x@".$_SERVER['SERVER_NAME'].$request_path);
 } elseif(@$_SERVER['PHP_AUTH_USER']!=$admin_user || @$_SERVER['PHP_AUTH_PW']!=$admin_password) {
  header('WWW-Authenticate: Basic realm="Please login"');
  header('HTTP/1.0 401 Unauthorized');
  die("Access forbidden");
 }

 echo '<h1>This page protected by http authorization.</h1>';

 echo '<a href="'.$request_path.'?logout">logout</a>';
?>

Кстати, трикс придумал не я, а подсмотрел его у Jacob Wright.

среда, 10 марта 2010 г.

Как я делал динамический список на jQuery #2

Продолжение поста "Как я делал динамический список на jQuery #1".

Итак, я немного окультурил динамический список теперь их может быть несколько на странице, за счет того, что обращение идет не через селектор id, а через селектор class. Впрочем, id тоже остался, но теперь это в прямом смысле id списка, который передается php-скрипту. А тот в свою очередь теперь умеет обрабатывать два списка.
Кроме того html-код, которым оперирует скрипт, теперь вынесен в переменные-шаблоны в начале скрипта и редактировать его будет удобнее. Главное помнить, что на место подстановочной последовательности %%index%% будет подставлен id элемента, а на место %%data%% его содержимое (подстановочные символы заменяются функцией .replace).
Блок добавления нового элемента внесен в список, в связи с чем появилась функция .before
Все остальное, за исключением некоторых изменений в селекторах, осталось неизменным.

В php-скрипте добавлена поддержка нескольких списков. Кроме того, в начале скрипта добавлена строка:
if($_SERVER['HTTP_X_REQUESTED_WITH']!='XMLHttpRequest') exit();

Она прерывает выполнение скрипта, если скрипт вызван не AJAX-запросом. Что логично, так как наш скрипт других запросов не обрабатывает (подробнее тут: http://www.rsdn.ru/article/inet/jQuery.xml).

Ну, и код на JavaScript теперь лежит в отдельном файле. Если это еще не framework работы с динамическими списками, то уже очень на него похож.

P.S.: Несмотря на то, что php-скрипт теперь защищен от обычных (не AJAX) запросов это не значит что скрипт стал безопасным. В нем по прежнему множество уязвимостей и использовать его для ваших целей в таком виде не рекомендуется.

Естественно, выкладываю все файлы в архиве.

пятница, 5 марта 2010 г.

Как я делал динамический список на jQuery #1

Все началось с того, что мне нужно было сделать динамический список, то есть такой список элементы которого я бы мог редактировать и удалять, а так же добавлять новые.
- Черт возьми! - думал я. - Как я устал от того, что любое нажатие, даже вход в режим редактирования элемента списка, вызывает перезагрузку страницы! С этим определенно нужно что-то делать.
Я знал, что существует такая библиотека, как jQuery, но это все что я про нее знал. К тому же я совсем не владею JavaScript.
- Ладно, наверняка Google нам поможет. - подумал я и открыл Яндекс.
(Я тут не буду дословно приводить как именно я искал, в наше время "жужлить" должны уметь все. Так же, как правило, я открывал первые результаты в поиске, так что приводимые ссылки никак нельзя назвать "лучшее по теме", это просто первое, что нашлось.)
Первая серия ссылок очень помогла получить общее представление о jQuery:
(в действительности мне понадобились только первая и третья части)
И тут уже можно было с чего-то начинать. Во-первых я скачал библиотеку jQuery (официальный сайт).

Во-вторых сделал файл index.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>jQuery list demo</title>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<script type="text/javascript" src="jquery-1.3.2.js"></script>

</head>
<body>

<h1>List</h1>
<hr />

<ul id="dynamic_list_1">
<li id="0">First item</li>
<li id="1">Second item</li>
<li id="2">Third item</li>
</ul>

</body>
</html>

1. Все-таки динамический список
Это был совсем не динамический список. Я решил для начала сделать так, чтобы элементы списка подгружались динамически, для этого в заголовок страницы я добавил такой скрипт:
<script type="text/javascript">
$(document).ready(function(){ 
 $.getJSON('db.php', {act: "getall"}, function(json){

  $('#dynamic_list_1').html('');

  $.each(json,function(index,data){
   $('#dynamic_list_1').append(
    '<li id="' + index + '">' +
    '<div class="item_content">' + data + '</div>' +
    '<div class="controls"><a href="#" id="edit">edit</a> <a href="#" id="delete">delete</a></div>' +
    '</li>\n'
   );
  });
 }); 
});
</script>

И тут я хотел бы сказать кое-что о синтаксисе. Наверняка это хорошо известно тем, кто программирует на JavaScript, но меня тут ожидало большое открытие. Это передача функции и ее кода как параметра другой функции.
Смотрите, вот с самого начала мы вызываем функцию ready для документа, которая будет вызвана браузером сразу же, но какие параметры мы ей передаем? Мы ей передаем функцию, которая должна быть вызвана как только DOM документа будет полностью загружен. Если убрать все несущественное, то останется только:
jQuery(document).ready( function(){ /*...*/ } );
Вот эта function(){ /*...*/ } и есть тот параметр, который мы передали в функцию ready и она теперь "знает" какой код нужно выполнять когда DOM будет загружен.
Если вы никогда не использовали такие штуки, то вам может показаться, что это все очень не удобно, но стоит привыкнуть и вы поймете, что на самом деле это очень изящная парадигма передачи функций как параметров. К тому же весь jQurey построен на этом принципе.
Еще один пример встречается чуть ниже: итератор each.
jQurey.each(json,function(index,data){ /*...*/ });
Тут вызывается функция each, которой передается переменная-коллекция json и функция, которая будет вызвана на каждой итерации, в которую, в свою очередь, будет передан индекс (index) элемента коллекции и сам элемент коллекции в переменную data.
Кстати, про each я жужлил отдельно и нашел информацию на api.jquery.com. Запомните этот адрес там мно-о-о-ого полезного.

Вернемся к коду. Основная функция тут getJSON. Она делает вызов db.php и при помощи метода GET, передает в скрипт параметр с именем act и значением "getall". Так же функции getJSON передается функция, которая будет вызвана в случае успешной передачи данных в которую, в свою очередь, будет передана информация, которую вернул скрипт.

Строки $('#dynamic_list_1').html(''); и $('#dynamic_list_1').append('...'); подробно разбирать не будем. Первая очищает все внутри нашего списка, вторая добавляет в него элементы, полученные от скрипта db.php

Кстати, скрипт db.php по смыслу будет "серверной стороной" нашего списка и будет обслуживать запросы от index.html: изменять БД с нашим списком. Для примера, пусть список хранится в простом текстовом файле items.txt:
First item
Second item
Third item

А вот сам db.php
<?php
 $act=$_GET['act'];
   
 file_put_contents('debug.txt',var_export($_GET,true));
 
 if($act=='getall') {
  $items=file('items.txt');
  $items = array_map("rtrim", $items);
  echo json_encode($items);
 } 
?>

Запись в файл debug.txt (строка 4) тут нужна исключительно для отладки, чтобы отслеживать запросы ajax.
Функция json_encode сворачивает данные php в формат JSON (о формате JSON вы можете почитать в той серии ссылок, что я приводил выше, а о json_encode на оф.сайте php.net) А данные, в свою очередь, будет получены и переданы в итератор each, как я уже писал выше.

Итак, в двух словах: мы сделали код, который после загрузки страницы index.html обратится к скрипту db.php, получит у него список и выведет его.

2. Удалить элемент
Вы наверняка заметили, что в каждый элемент я добавил две ссылки: edit и delete. По нажатию на эти ссылки элемент должен редактироваться и удаляться, соответственно. Начнем с удаления, потому что это проще.

Реализовать удаление я предполагал следующим образом: послать запрос на удаление в php скрипт с индексом удаляемого элемента и если запрос успешно отработан удалить элемент со страницы. Для этого в index.html я добавил такой код:

$("div.controls #delete").live("click", function(){

  var item=$(this).parents("li");
  var item_id = item.attr('id'); 
  
  $.get('db.php', {act: 'del', id: item_id}, function(){
   item.animate({ opacity: 'hide' }, "fast");
   item.remove();
  });
    
 });

Давайте разберемся. Во-первых несмотря на то, что для отлавливания клика в "jQuery для начинающих. Часть 1." используется метод .click я использовал метод .live. Его я нашел на сайте перевода официальной документации. Разница в том, что .click работает только для статических элементов страницы, а .live так же и для тех, что были созданы самим скриптом.
В третьей строке мы получаем тэг-родитель li, собственно весь элемент списка, а в четвертой его id. (Как получить свойства тэга я нашел тут).
А дальше следует метод вызов метода .get (описание тут). Я решил не использовать .getJSON, поскольку мне не нужно принимать сложную структуру данных, а только убедиться в том, что вызов прошел успешно. В качестве параметров скрипту передается act="del" и id с номером элемента списка.
После, если вызов прошел успешно, со страницы удаляется элемент списка. Метод .animate я взял из "jQuery для начинающих", а .remove с форума JavaScript.ru.

db.php так же придется доработать. Я дополнил условие следующим кодом:
} elseif ($act=='del') {
  $items=file('items.txt');
  
  unset($items[$_GET['id']]);
  
  $items = array_map("rtrim", $items);
  $str = implode("\n", $items);
  file_put_contents ("items.txt", $str);
 }
Тут все очевидно, поэтому останавливаться не будем.

Теперь вы можете удалять элементы из списка.

3. Добавить элемент
К этому моменту у меня уже было достаточно практики и вычитанных манов, чтобы с добавлением никаких сложностей не возникло.

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

Дополним index.html формой для ввода данных нового элемента. Для этого после нашего списка вставим такой код:
<div class="add_item">
 <input class="item_content" type="text" value="">
 <div class="controls"><a href="#" id="add">add</a></div>
</div>

А в скрипт вставим обработку:
$("div.add_item div.controls #add").click(function(){

  $("#dynamic_list_1 li.editing").remove();
  $("#dynamic_list_1 li").show();
  
  var context=$("div.add_item .item_content");
  
  $.get('db.php', {act: 'add', cont: context.attr('value')}, function(index){
   $('#dynamic_list_1').append(
          '<li id="' + index + '">' +
       '<div class="item_content">' + context.attr('value') + '</div>' +
       '<div class="controls"><a href="#" id="edit">edit</a> <a href="#" id="delete">delete</a></div>' +
       '</li>\n'
      );
   context.attr('value','');
  });

 });

Тут все достаточно очевидно, кроме 3-й и 4-й строки, которые мы рассмотрим, когда будем добавлять редактирование элементов.
В переменную context мы сразу запоминаем элемент страницы input в котором хранятся данные для нового элемента списка, потом делаем запрос к скрипту db.php, который возвращает номер добавленного элемента. И добавляем новый элемент на страницу. В самом конце поле input очищается (установка аттрибутов).

В db.php тоже добавим обработку вызова:
} elseif ($act=='add') {
  $items=file('items.txt');

  $items[]=$_GET['cont'];
  echo count($items)-1;
  
  $items = array_map("rtrim", $items);
  $str = implode("\n", $items);
  file_put_contents ("items.txt", $str);
 }

4. И, наконец, редактировать элемент
С редактированием элемента будет лишь немного сложнее, чем со всем остальным. Редактирование мы разделим на два этапа:
  1. Вход в режим редактирования
  2. Сохранение результатов, выход из режима редактирования

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

Для этого в index.html добавим такой скрипт:

$("div.controls #edit").live("click", function(){

  $("#dynamic_list_1 li.editing").remove();
  $("#dynamic_list_1 li").show();
  
  var item=$(this).parents("li");
  var item_id = item.attr('id');
  var context=item.children('div.item_content'); 
  
  item.after(
   '<li class="editing" id="' + item_id + '">' +
   '<input class="item_content" type="text" value="' + context.html() + '">' +
   '<div class="controls"><a href="#" id="ok">ok</a> <a href="#" id="cancel">cancel</a></div>' +
   '</li>\n'
  );
  item.hide();
      
 });


По щелчку на ссылке edit скрипт добавляет код с полем ввода после элемента в котором кликнули edit (метод .after), а потом скрывает сам элемент методом .hide.

Третья и четвертые строки тут нужны вот для чего: если какой-то элемент уже был в режиме редактирования, то третья строка удаляет поле ввода редактирования, а четвертая показывает элемент, который был скрыт так как редактировался. Таким образом предыдущее редактирование отменяется и только после этого в режим редактирования "входит" новый элемент.
Аналогичные есть строки есть в блоке добавления элемента (см выше). Это нужно для сброса режима редактирования при добавлении новых элементов списка. В принципе это не обязательно, но интерфейсно понятнее для пользователя.

Добавим обработку ссылки cancel для режима редактирования:

$("li.editing div.controls #cancel").live("click", function(){
  $("#dynamic_list_1 li.editing").remove();
  $("#dynamic_list_1 li").show();
 });

И, наконец, отправка информации, которую мы отредактировали:

$("li.editing div.controls #ok").live("click", function(){

  var item=$(this).parents("li"); 
  var item_id = item.attr('id'); 
  var context=item.children("input.item_content");
  
  $.get('db.php', {act: 'edit', id:item_id, cont: context.attr('value')}, function(){
   item.after(
    '<li id="' + item_id + '">' +
    '<div class="item_content">' + context.attr('value') + '</div>' +
    '<div class="controls"><a href="#" id="edit">edit</a> <a href="#" id="delete">delete</a></div>' +
    '</li>\n'
   );
   item.remove();
   $("#dynamic_list_1 li:hidden").remove();
   
  });

 });

Общая логика такова: мы отправляем скрипту db.php информацию о редактировании с id редактируемого элемента и содержимым поля input. В случае успешного завершения вызова добавляем новый отредактированный элемент в список, а старый элемент и поле ввода режима редактирования удаляем.

Соответственно нужно добавить код и в db.php:
} elseif ($act=='edit') {
  $items=file('items.txt');
  
  $items[$_GET['id']]=$_GET['cont'];
  
  $items = array_map("rtrim", $items);
  $str = implode("\n", $items);
  file_put_contents ("items.txt", $str);
 }

Вот, собственно и все. Теперь у нас есть страница со списком, который мы можем редактировать не перегружая самой страницы.

Те, кто не захотел разбираться могут скачать все файлы в архиве и посмотреть как оно все работает уже готовое.

Конечно, приведенный код далеко не оптимальный, возможно имеет баги и проблемы безопасности, поэтому его нельзя использовать в ваших проектах. К тому же для работы со списками (и даже таблицами) есть специализированные framework-и созданные профессионалами. А этот пример нужен только для того, чтобы продемонстрировать основные принципы работы с jQuery и динамическими списками. А так же продемонстрировать то, что весь мир находится на грани катастрофы, потому что если кто-то захочет поработить человечество ему достаточно будет только пожужлить.

Итак, согласно принципам agile мы максимально быстро наупырили какое-то количество корявого кода. На следующем шаге я приведу его в порядок и обязательно поделюсь с вами.

Всем спасибо.

UPD.: Как я делал динамический список на jQuery #2

четверг, 25 февраля 2010 г.

gettext

Наконец-то я окончательно разобрался с модулем gettext. Все работает, хак против кеширования показал себя отлично, правда в винде нужно заклюшить (@) некоторые setlocale.

А небольшой скрипт, по добавлению timestamp-ов к именам файлов, для этого хака как раз позволяет не обращать внимание на то, что gted не работает с папкой LC_MESSAGES. Скрипт сам переместит файлы в нужное место.

понедельник, 15 февраля 2010 г.

DrupalCamp Kyiv 2010

В этом году снова будет проходить DrupalCamp. Увидимся на месте!

понедельник, 8 февраля 2010 г.

Проблема с интернационализацией.

В процессе написания небольшого скрипта на PHP решил, что будет хорошо, если скрипт будет поддерживать несколько языков.
По первому взгляду вопрос решается обычным ассоциативным массивом. Но в этом мире и без меня достаточно изобретателей велосипедов, поэтому следовало посмотреть gettext. Инструмент мне понравился, особенно утилита xgettext, но его настройка пока мне не дается.
Возникли таки проблемы:
  1. Хороший плагин для eclipse gted по-умолчанию формирует .po/.mo-файлы в папке ./locale/<код_языка>, а скрипт (считай модуль апача) подтягивает файлы из папки ./locale/<код_языка>/LC_MESSAGES. Неудобно то, что файлы приходится перекладывать.
  2. На моем EasyPHP  файлы перевода подтягиваются только после перезагрузки сервера. Понятно, что для продакшн-сервера это будет неудобно.
  3. Ну, и никто таки не смог мне внятно объяснить смысл установки локали (setlocale) и переменной окружения (putenv, LANG). Этот вопрос явно мной недокурен в достаточной степени.
Если эти вопросы не решатся, то мой велосипед будет функцией, которая парсит .po-файлы. Удобно тем, что можно использовать тот же xgettext, но не нужен отдельный модуль для Apache. С другой стороны быстродействие... Не знаю, не знаю...

P.S.: Кстати, для модуля i18n, например для drupal, файлы перевода это только .po-файлы. К чему бы это?

P.P.S.: Я был прав насчет перегрузки сервера. Но есть интересный хак при помощи которого можно обойти кеширование .mo-файлов. И с локалью все стало понятно.

среда, 13 января 2010 г.

Мануал по RegExp

Совершенно непонятно зачем с php.net убрали толковый ман по regexp-ам и заменили его бестолковым.
Зато в википедии появился замечательный ман. Ходите туда, товарищи.