Ярлыки

.htaccess (4) тестирование (8) шаблоны проектирования (3) css (5) Debian (6) docker (2) Doctrine2 (6) Git (6) html (4) java (6) javascript (13) jquery (11) LFS (3) linux (23) mac os (4) mod_rewrite (2) MSSQL (4) MySQL (18) ORM Doctrine (17) patterns (3) PDO (3) perl (7) PHP (64) PHPUnit (8) Python (15) SEO (2) Silex (1) SimpleXML (1) SQL (14) ssh (4) Ubuntu (24) Yii1 (1) Zend Framework (19) ZendFramework2 (8)

пятница, 3 декабря 2010 г.

Карлос Саура. Выкорми ворона. Cria Cuervos (Raise ravens) .

Jquery. Как вернуть значение, полученное как ответ ajax-запроса.

Необходимо задать запросу параметр async: false, он будет ждать ответ.
Может повесить браузер пользователя.

...
var result;
$.ajax({
    type:   'GET',
    url:    '/path/to/server/script.php',
    async:  false,
    success: function (data) { result = data }
});
return result;
...

Javascript. Передача параметров функции по-умолчанию.

function foo(a, b)
{
    a = typeof(a) != 'undefined' ? a : 42;
    b = typeof(b) != 'undefined' ? b : 'default_b';
    ...
}

среда, 1 декабря 2010 г.

PHP. Определение ajax-запроса на стороне сервера.

Метод возвращает true или false в зависимости от тога, является ли запрос ajax или нет.
class Request
{
...
    public function isXmlHttpRequest() {
        return !empty($_SERVER['HTTP_X_REQUESTED_WITH']) &&
            (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest');
    }
...
}

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

Debian. Сверим часы.

sudo date +%T -s "14:37:00"

Debian. Установка memcache.

apt-get install memcached
ps aux | grep memcache
nobody    9224  0.0  0.0   2584  1084 ?        S    13:53   0:00 /usr/bin/memcached -m 64 -p 11211 -u nobody -l 127.0.0.1

Качаем Development package xampp и исходники модуля memcache для php c http://pecl.php.net/package/memcache.
Распаковываем:
debian:tar xvfz xampp-linux-devel-1.7.3a.tar.gz -C /opt 
debian:/home/leon/Downloads# exit
exit
leon@debian:~/Downloads$ tar xzf memcache-3.0.5.tgz 
leon@debian:~/Downloads$ cd memcache-3.0.5
leon@debian:~/Downloads/memcache-3.0.5$ sudo /opt/lampp/bin/phpize
Configuring for:
PHP Api Version:         20090626
Zend Module Api No:      20090626
Zend Extension Api No:   220090626
Cannot find autoconf. Please check your autoconf installation and the
$PHP_AUTOCONF environment variable. Then, rerun this script.

leon@debian:~/Downloads/memcache-3.0.5$ sudo apt-get install autoconf
...
leon@debian:~/Downloads/memcache-3.0.5$ sudo /opt/lampp/bin/phpize
Configuring for:
PHP Api Version:         20090626
Zend Module Api No:      20090626
Zend Extension Api No:   220090626
leon@debian:~/Downloads/memcache-3.0.5$ 
leon@debian:~/Downloads/memcache-3.0.5$ ./configure --enable-memcache --with-php-config=/opt/lampp/bin/php-config
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for a sed that does not truncate output... /bin/sed
checking for cc... no
checking for gcc... no
configure: error: no acceptable C compiler found in $PATH
See `config.log' for more details.
leon@debian:~/Downloads/memcache-3.0.5$ sudo apt-get install gcc
...
leon@debian:~/Downloads/memcache-3.0.5$ ./configure --enable-memcache --with-php-config=/opt/lampp/bin/php-config
...
checking for the location of zlib... configure: error: memcache support requires ZLIB. Use --with-zlib-dir=<DIR>
to specify prefix where ZLIB include and library are located
...
leon@debian:~/Downloads/memcache-3.0.5$ sudo apt-get install zlib1g-dev
...
leon@debian:~/Downloads/memcache-3.0.5$ ./configure --enable-memcache --with-php-config=/opt/lampp/bin/php-config
leon@debian:~/Downloads/memcache-3.0.5$ sudo apt-get install make
leon@debian:~/Downloads/memcache-3.0.5$ make
leon@debian:~/Downloads/memcache-3.0.5$ sudo make install
...
Installing shared extensions:     /opt/lampp/lib/php/extensions/no-debug-non-zts-20090626/
leon@debian:~/Downloads/memcache-3.0.5$ sudo nano /opt/lampp/etc/php.ini
Добавляем расширение:
extension="memcache.so"
Перезапускаем xampp ...

Debian. Установка Firefox.

Качаем архив с сайта firefox для linux.

leon@debian:~/Downloads$ su
Пароль: 
debian:/home/leon/Downloads# mv firefox-3.6.12.tar.bz2 /usr/lib
debian:/home/leon/Downloads# cd /usr/lib/
debian:/usr/lib# tar -jxvf firefox-3.6.12.tar.bz2
debian:/usr/lib# ln -s /usr/lib/firefox/firefox /usr/bin/firefox

Создаем ссылку на каталог с плагинами:
debian:/usr/lib# rm -rf /usr/lib/firefox/plugins
debian:/usr/lib# ln -s /usr/lib/mozilla/plugins /usr/lib/firefox/plugins

Debian. Подключаем backports и устанавливаем Pidgin.

Некоторые пакеты с более новыми версиями для Lenny доступны из репозитория backports.

leon@debian:~$ su
Пароль: 
debian:/home/leon# nano /etc/sudoers 

Добавляем в файл строку:
leon ALL=(ALL) ALL
Этим мы позволяем пользователю пользоваться командой sudo.

debian:/home/leon# exit
exit
leon@debian:~$ sudo nano /etc/apt/sources.list

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

[sudo] password for leon:

Добавляем репозиторий в список:
deb http://www.backports.org/debian lenny-backports main contrib non-free

leon@debian:~$ sudo apt-get update
leon@debian:~$ sudo apt-get install debian-backports-keyring

Устанавливаем Pidgin
leon@debian:~$ apt-get install -t lenny-backports pidgin

Теперь установлен Pidgin 2.7.3.

воскресенье, 31 октября 2010 г.

Zend Framework. Элемент multiselect и сообщение об ошибке ' ... was not found in haystack' .

Используя Zend_Form_Element_Select или Zend_Form_Element_Multiselect я столкнулся со странным сообщением об ошибке при попытке отправить форму:

'1' was not found in the haystack

Никаких валидаторов у меня не было.
Оказывается по-умолчанию включен валидатор 'RegisterInArrayValidator'.
Который можно просто отключить:

$groups = new Zend_Form_Element_Multiselect('myGroups');
$groups->setLabel('Опубликовать в группах');
$groups->setRegisterInArrayValidator(false); 
$form->addElement($groups);

суббота, 30 октября 2010 г.

CSS. crop изображения с помощью clip.

Изменение размера изображения без искажений.
Свойство clip позволяет задать размеры абсолютно позиционированного элемента img.
Элемент должен быть видимым (visible).
img
{
    position: absolute;
    clip: rect(0px, 60px, 200px, 0px); /* rect (top, right, bottom, left) */
}

пятница, 29 октября 2010 г.

ORM Doctrine. Сортировка объектов в коллекции.

Преобразуем Doctrine_Collection в массив:

$user = Doctrine::getTable('User')->find(1);
$blogEntries = $user->BlogEntries->getData();

Создаем собственную функцию сортировки:

usort($blogEntries, array(__CLASS__, '_compareTimeDesc'));

....

private static function _compareTimeDesc($a, $b) {
    // time - поле базы данныx (время публикации)
    if ($a->time == $b->time) {
      return 0;
    }
    return ($a->time < $b->time) ? 1 : -1;
}

XAMPP. Обновление PHP для Windows.

22 октября вышли новые beta версии XAMPP. Теперь доступен PHP 5.3.3.

Linux (beta8):
  • Apache (2.2.17)
  • MySQL (5.1.51)
  • PHP (5.3.3)
  • ProFTPD (1.3.3b)
  • phpMyAdmin (3.3.7)
Windows (beta2):
  • Apache (2.2.16)
  • MySQL (5.1.49)
  • PHP (5.3.3)
  • phpMyAdmin (3.3.5)
Качаем тут: apachefriends.org

Сохраняем содержимое C:\xampp\htdocs и C:\xampp\mysql\data. Обновляем и копируем поверх наши каталоги.

PHPUnit.Тестирование private и protected методов.

Если используется PHP5 (>= 5.3.2) и PHPUnit возможно тестирование защищенных методов класса с помощью reflection. При этом методы становятся public перед запуском тестов.

protected static function getMethod($name) 
{
  $class = new ReflectionClass(MyClass::class);
  $method = $class->getMethod($name);
  $method->setAccessible(true);
  return $method;
}

public function testFoo() 
{
  $foo = self::getMethod('foo');
  $obj = new MyClass();
  $foo->invokeArgs($obj, [...]);
  ...
}

четверг, 28 октября 2010 г.

Шаблон проектирования Facade (Фасад).

Шаблон Facade (Фасад) — Шаблон проектирования, позволяющий скрыть сложность системы путем сведения всех возможных внешних вызовов к одному объекту, делегирующему их соответствующим объектам системы.




class Foo {
  public function bar() {
    echo "Foo::bar()\n";
  }
}

class Bar {
  public function foo() {
    echo "Bar::foo()\n";
  }
}

class Facade {
  protected $_foo = null;
  protected $_bar = null;

  public function __construct() {
    $this->_foo = new Foo();
    $this->_bar = new Bar();    
  }

  public function bar() {
    $this->_foo->bar();
  }

  public function foo() {
    $this->_bar->foo();
  }
}


$facade = new Facade();
$facade->foo();
$facade->bar();

вторник, 26 октября 2010 г.

Zend Framework. Автоматическая загрузка классов с Zend_Loader.

Устанавливаем include path:
set_include_path('/путь/к/Zend' . PATH_SEPARATOR . get_include_path());

Подключаем класс автозагрузчика:
require_once 'Zend/Loader/Autoloader.php';

Устанавливаем параметры:

// Получаем объект загрузчика
$loader = Zend_Loader_Autoloader::getInstance();

// Определяем префиксы имен классов, которые хотим загружать
// 'Zend_' и 'ZendX_' уже включены по-умолчанию
$loader->registerNamespace('My_App_');

// Параметр, указывающий, что нужно загружать все пространства имен (необязательный)
$loader->setFallbackAutoloader(true);

Делаем это в bootstrap или еще где-то, теперь автозагрузка должна работать:

$foo = new Zend_Library_Class();
$bar = new My_App_Class();

суббота, 23 октября 2010 г.

LINUX FROM SCRATCH. Сборка GCC - этап 1.

pwd (англ. print working directory — напечатать рабочий каталог) — консольная утилита в UNIX-подобных системах, которая выводит полный путь от корневого каталога к текущему рабочему каталогу.

Предварительные действия:

1. Распаковываем архив gcc в $LFS/sources
2. Переходим в каталог $LFS/sources/gcc-4.5.1
3. Распаковываем архивы gmp, mpc и mpfr в каталог gcc-4.5.1
4. Создаем каталог $LFS/sources/gcc-build (../gcc-build)
5. Переходим в gcc-build

Настройка:

../gcc-4.5.1/configure \
--target=$LFS_TGT --prefix=/tools \
--disable-nls --disable-shared --disable-multilib \
--disable-decimal-float --disable-threads \
--disable-libmudflap --disable-libssp \
--disable-libgomp --enable-languages=c \
--with-gmp-include=$(pwd)/gmp --with-gmp-lib=$(pwd)/gmp/.libs \
--without-ppl --without-cloog

--disable-shared
Библиотеки линкуются статически.

--disable-decimal-float, --disable-threads, --disable-libmudflap,
--disable-libssp, --disable-libgomp
Отключаем поддержку функций в которых нет необходимости.

--enable-languages=c
Пока нам нужен только C.

--with-gmp-include=...
Указываем местонахождение заголовков GMP ...

--with-gmp-lib=...
... и библиотеки

Далее ...

make && make install

Using - - disable- shared means that the libgcc_ eh. a file isn't created and installed. The Glibc package
depends on this library as it uses - lgcc_ eh within its build system. This dependency can be satisfied by creating a
symlink to libgcc. a, since that file will end up containing the objects normally contained in libgcc_ eh. a:
ln -vs libgcc.a `$LFS_TGT-gcc -print-libgcc-file-name | \
sed 's/libgcc/&_eh/'`

LINUX FROM SCRATCH. Сборка.

Важные замечания.
1. После установки каждого пакета необходимо удалить каталог с исходниками и каталог для сборки.
2. Для сборки используется bash.
3. Перед сборкой распаковываем архив, как пользователь lfs и переходим в каталог.

tar xvzf file-1.0.tar.gz - распаковать gzip tar файл (.tgz или .tar.gz)
tar xvjf file-1.0.tar.bz2 - распаковать bzip2 tar файл (.tbz или .tar.bz2)
tar xvf file-1.0.tar - распаковать tar файл (.tar)

x = eXtract, определяет что архив должен быть распакован ( c = create для создания )
v = подробный вывод (не обязательный параметр)
z = gzip-архив; j = bzip2-архив
f = из/в файл ... (то что после параметра f)

Сборка Binutils-2.20.1 - этап 1.

Помимо прочего содержит ассемблер и линкер.
Документация рекомендует собирать binutils в отдельной директории, поэтому:

mkdir -v ../binutils-build
cd ../binutils-build

Далее готовим binutils к компиляции:

../binutils-2.20.1/configure \
--target=$LFS_TGT --prefix=/tools \
--disable-nls --disable-werror --build='i386-pc-linux'

Я собираю на Debian на Intel P4  (Oracle VirtualBox 3.2.10 под Windows XP SP3), поэтому в ./configure для binutils я добавил опцию --build='i386-pc-linux'

--target=$LFS_TGT

--prefix=/tools
Готовим к установке в /tools

--disable-nls
Запрещаем интернационализацию, оно нам не надо.

--disable-werror
Запрещаем остановку сборки при warning компилятора хост-системы.

Компиляция:
make

Установка:
make install

LINUX FROM SCRATCH. Установка окружения.

Создаем файл конфигурации bash для нового пользователя lfs в его домашнем каталоге:

cat > ~/.bash_profile << "EOF" exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash 
EOF 

Наша задача создать абсолютно независимое от хост-системы окружение, для этого создаем .bashrc:

cat > ~/.bashrc << "EOF"
set +h
umask 022
LFS=/mnt/lfs
LC_ALL=POSIX
LFS_TGT=$(uname -m)-lfs-linux-gnu
PATH=/tools/bin:/bin:/usr/bin
export LFS LC_ALL LFS_TGT PATH
EOF

set +h
Запрещаем кеширование путей к исполняемым файлам, шелл всегда будет проверять пути в переменной PATH и находить все только что скомпилированные инструменты в каталоге tools. Таким образом мы избежим использования инструментов хост-системы.

umask 022
Все вновь созданные файлы доступны для записи только владельцем, а для чтения и запуска кем угодно.

LFS=/mnt/lfs
Переменная указывает путь к каталогу к которому монтируется раздел будущей системы.

LC_ALL=POSIX
Устанавливаем локализацию приложений.

LFS_TGT=$(uname -m)-lfs-linux-gnu
Описание, используемое при сборке компилятора и линкера.

PATH=/tools/bin:/bin:/usr/bin
Первым в переменной пути идет каталог с инструментами для сборки системы.

Когда все готово, создаем профиль пользователя:

source ~/.bash_profile

вторник, 19 октября 2010 г.

LINUX FROM SCRATCH. Создание и монтирование разделов. Продолжение.

Создаем каталог для исходников и сборки:
mkdir -v $LFS/sources

... и выставляем необходимые права:
chmod -v a+wt $LFS/sources

Качаем исходники:
wget ftp://ftp.aliensoft.org/pub/lfs/lfs-packages/lfs-packages-6.7.tar

Создаем директорию для компиляции инструментов. Мы отделяем то, что не будет частью будущей системы ...
mkdir -v $LFS/tools

... создаем ссылку на только что созданную директорию:
ln -sv $LFS/tools /

Для того, чтобы случайно не повредить систему, создаем новую группу и нового пользователя:
groupadd lfs
useradd -s /bin/bash -g lfs -m -k /dev/null lfs

-s /bin/bash
По умолчанию shell нового пользователя - bash

-g lfs
Включаем его в группу lfs

-m
Создаем домашний каталог нового пользователя

-k /dev/null
Предотвращаем копирование шаблонных файлов из /etc/skel, перенаправляя вывод

lfs
Имя нового пользователя и группы

Задаем пароль нового пользователя:
passwd lfs

Делаем его владельцем каталога $LFS/tools:
chown -v lfs $LFS/tools

Делаем его владельцем каталога $LFS/sources:
chown -v lfs $LFS/sources

Заходим, как новый пользователь:
su - lfs

понедельник, 18 октября 2010 г.

LINUX FROM SCRATCH. Создание и монтирование разделов.

Список дисков fdisk -l:


Создаем root раздел и swap с помощью cfdisk.




Создаем файловую систему в корневом разделе с помощью mke2fs.



Форматируем swap раздел c mkswap /dev/hda2.
Монтируем раздел:
export LFS=/mnt/lfs
mkdir -pv $LFS
mount -v -t ext2 /dev/hda1 $LFS

Разрешаем использование swap:
/sbin/swapon -v /dev/hda2

суббота, 16 октября 2010 г.

Perl - PHP разговорник. Соответствие синтаксических конструкций.



Perl PHP
Массивы

@a = ();
 
@a = ( 'xx', 11, 33.5, );
 
@a = 12..33;
 
$a[2] = 'something';
 
$len = scalar(@a);
# or
$len = @a;
 
@a3 = ('xx', @a1, @a2);
 
($x, $y) = @a;
 
$a[@a] = 'new'; # push
 
push
pop
shift
unshift
splice
 
foreach $i (@a) { .. }

$a = array();
 
$a = array( 'xx', 11, 33.5, );
 
$a = range(12,33);
 
$a[2] = 'something';
 
$len = count($a);
 
 
 
$a3 = array_merge('xx', $a1, $a2);
 
list($x, $y) = $a;
 
$a[] = 'new'; # push
 
array_push
array_pop
array_shift
array_unshift
array_splice
 
foreach ($a as $i) { .. }
Ассоциативные массивы (хэши)

%h = ();
 
%h = ( 'x' => 'y',
       'z' => 'w',
     );
 
$h{'x'} = 7;
 
while (($key,$value) = each(%h))
{ .. }
 
$a = keys(%h);
$b = values(%h);
 
delete $h{'x'};

$h = array();
 
$h = array( 'x' => 'y',
            'z' => 'w',
          );
 
$h['x'] = 7;
 
foreach ($h as $key => $value)
{ .. }
 
$a = array_keys($h);
$b = array_values($h);
 
unset( $h['x'] );

Структуры данных

%h = ('a'=>13, 'b'=>25);
@x = ('hi', 'there', 'all',);
 
@mix = ( \%h, \@x,
         [33..39],
  { x=>15, yy=>23, },
       );
 
$mix[0]->{'b'}  # == 25
$mix[0]{'b'}    # == 25
$mix[2]->[2]    # == 35
$mix[2][2]      # == 35


$h = array('a'=>13, 'b'=>25);
$x = array('hi', 'there', 'all',);
 
$mix = array($h, $x,
      range(33,39),
      array('x'=>15, 'yy'=>23),
     );
 
$mix[0]['b']  # == 25
 
$mix[2][2]    # == 35

Массивы split/join

@a = split( '\|', $s );
 
@a = split( '\s+', $s );
 
 
$s = join( '|', @a );

$a = preg_split( '/\|/', $s,
            -1, PREG_SPLIT_NO_EMPTY );
$a = preg_split( '/\s+/', $s,
            -1, PREG_SPLIT_NO_EMPTY );
 
$s = join( '|', $a );
Перевод строки в нижний/верхний регистр

$s = lc($s);
$s = uc($s);
 
$s =~ tr/a-z/A-Z/;

$s = strtolower($s);
$s = strtoupper($s);
Сравнение строк

$s1 eq $s2
 
 
 
$s1 lt $s2

strcmp($s1,$s2) == 0
# or
$s1 === $s2
 
strcmp($s1,$s2) < 0
Функции

sub foo {
 my @args = @_;
}
 
sub foo {
 $x = 5;
}
 
 
 
 
 
foo2( \@a, \%h );

function foo() {
 $args = func_get_args();
}
 
function foo() {
 global $x;
 $x = 5;
}
 
function foo2($x, $y) {
}
 
foo2( $a, $h );
Операции с регулярными выражениями

$s =~ m/(\w+)/;
$substr = $1;
 
@all = ($s =~ m/(\w+)/g);
 
 
$s =~ s/\s+/X/;
$s =~ s/\s+/X/g;
 
$s =~ s/^\s+|\s+$//g;


preg_match( "/(\w+)/", $s, $match );
$substr = $match[1];
 
preg_match_all( "/(\w+)/", $s, $match );
$all = $match[0];
 
$s = preg_replace( "/\s+/", 'X', $s, 1 );
$s = preg_replace( "/\s+/", 'X', $s );
 
$s = trim($s);

basename/dirname

use File::Basename;
 
$b = basename($path);
$d = dirname($path);

$b = basename($path);
$d = dirname($path);
Переменные окружения

%ENV
 
$ENV{REQUEST_METHOD}
 
$ARGV[$i]
 
$0


$_SERVER
 
$_SERVER[REQUEST_METHOD]
 
$argv[$i+1]
 
$argv[0]  # только CGI

Параметры POST/GET

#form/hyperlink parameters:
# s : single-valued
# m : multi-valued
 
use CGI (:standard);
 
 
 
 
$s = param('s');
@m = param('m');
 
@param_names = param();
$num_params = param();


#form/hyperlink parameters:
# s   : single-valued
# m[] : multi-valued
#       (such as multi-selections
#        and checkbox groups)
 
$PARAM
  = array_merge($_GET, $_POST);
 
$s = $PARAM['s'];  # a scalar
$m = $PARAM['m'];  # an array
 
$param_names = array_keys($PARAM);
$num_params = count($PARAM);

Элементы HTML

use CGI (:standard);
 
 
 
 
 
 
 
$ref = "x.cgi";
a({href=>$ref}, "yy")
 
textfield({name=>"yy", size=>5})
 
password({name=>"yy", size=>5})
 
textarea({name=>"yy",
   cols=>5, rows=>2})
 
submit({value=>"yy"})
 
button( {name=>"xx",
  value=>"yy",
         onclick=>"submit()",
        }
      )
 
%labels = (0=>'a',1=>'q',2=>'x');
popup_menu( { name=>"xx",
              values=>[0..2],
              labels=>\%labels,
              size=>4,
     }
          )
 
 
@a = ('xx','yy','zz');
radio_group( { name=>'nn',
               values=> \@a,
               default=>'_',
               linebreak=>1,
      }
           )
 
%labels = ('xx'=>'L1','yy'=>'L2');
@a = keys( %labels );
checkbox_group( { name=>'nn',
                  values=> \@a,
    labels=> \%labels,
         }
              )
 
table(
       Tr(
           [
      td(['a','b']),
             td(['x','y']),
    ]
         )
     )


# The Perl/CGI functions have the
# additional property of "stability"
# when used in reentrant forms.
# The values of the HTML elements are
# set according to the incoming
# parameter values for those elements.
# The versions below are not stable.
 
$ref = "x.php";
<a href="<?php echo $ref?>">yy</a>
 
<input type=text name=yy size=5>
 
<input type=password name=yy size=5>
 
<textarea name=yy cols=5 rows=2>
</textarea>
 
<input type="submit" value=yy>
 
<input type="button"
  name="xx" value="yy"
  onclick="submit()">
 
 
 
<select name="xx" size="4">
<?php
$labels = array(0=>'a',1=>'q',2=>'x');
foreach (range(0,2) as $_)
  echo "<option value='$_'>",
       $labels[$_];
?>
</select>
 
$a = array('xx','yy','zz');
foreach ($a as $_)
  echo "<input type=radio
         name=nn value='$_'>$_<br>";
 
 
 
 
$labels = array('xx'=>'L1','yy'=>'L2');
foreach (array_keys($labels) as $_)
  echo "<input type=checkbox
         name=nn value='$_'>",
         $labels[$_];
 
 
 
<table>
<tr>
<td>a</td><td>b</td>
</tr>
<tr>
<td>x</td><td>y</td>
</tr>
</table>

urlencode

use URI::Escape;
 
uri_escape($val)
uri_unescape($val)

urlencode($val)
urldecode($val)
Работа с MySQL

use DBI;
$dbh = DBI->connect(
  'DBI:mysql:test:localhost',
  $usr,$pwd
);
 
$dbh->do( $sql_op )
 
$query = $dbh->prepare( $sql_op );
$query->execute();
 
while(
 @record = $query->fetchrow() )
{ .. }
 
 
$dbh->quote($val)


$dbh = mysql_connect(
  'localhost', $usr, $pwd
);
mysql_query('USE test')
 
mysql_query( $sql_op );
 
$results = mysql_query( $sql_op );
 
 
while($record =
        mysql_fetch_row($results))
{ .. }
 
 
"'" . addslashes($val) . "'"

пятница, 15 октября 2010 г.

Psoy Korolenko and Danik Redlick - The Unternationale greet Medinat Weimar

Биробиджан - Псой Короленко

PHPUnit.Тестирование кода, использующего Singletone.

Для начала посмотрим реализацию шаблона Singletone на php:
class Singleton
{
    private static $uniqueInstance = NULL;
 
    protected function __construct() {}
    private final function __clone() {}
 
    public static function getInstance()
    {
        if (self::$uniqueInstance === NULL) {
            self::$uniqueInstance = new Singleton;
        }
 
        return self::$uniqueInstance;
    }
}

Этот шаблон гарантирует, что код не будет доступен в приложении, иначе как через метод getInstance(). И клиент не сможет создать объект используя методы new или __clone.

class Client
{
    public function doSomething()
    {
        $singleton = Singleton::getInstance();
 
        // ...
    }
}

Очевидно, что невозможно написать тест для метода doSomething() без вызова getInstance(). Это значит, что мы не сможем создать новый объект для каждого из группы тестов и мы не сможем избежать побочных эффектов при тестировании.
Ниже приведены три варианта решения задачи:

Внедрение зависимости (Dependency Injection).

class Client
{
    public function doSomething(Singleton $singleton = NULL)
    {
        if ($singleton === NULL) {
            $singleton = Singleton::getInstance();
        }
 
        // ...
    }
}

Теперь вместо того, чтобы вызывать метод getInstance() мы можем подменить объект
класса Singletone mock-объектом или заглушкой (Stub).

class ClientTest extends PHPUnit_Framework_TestCase
{
    public function testSingleton()
    {
        $singleton = $this->getMock(
          'Singleton', /* name of class to mock     */
          array(),     /* list of methods to mock   */
          array(),     /* constructor arguments     */
          '',          /* name for mocked class     */
          FALSE        /* do not invoke constructor */
        );
 
        // ... configure $singleton ...
 
        $client = new Client;
        $client->doSomething($singleton);
 
        // ...
    }
}

Альтернативная реализация Singletone.

Вместо того чтобы модифицировать клиентский код для облегчения тестирования, мы может немного изменить реализацию одиночки, добавив дополнительный метод для обнуления объекта.
class Singleton
{
    private static $uniqueInstance = NULL;
 
    protected function __construct() {}
    private final function __clone() {}
 
    public static function getInstance()
    {
        if (self::$uniqueInstance === NULL) {
            self::$uniqueInstance = new Singleton;
        }
 
        return self::$uniqueInstance;
    }
 
    public static function reset() {
        self::$uniqueInstance = NULL;
    }
}

Singleton с тестовым контекстом.

class Singleton
{
    private static $uniqueInstance = NULL;
    public static $testing = FALSE;
 
    protected function __construct() {}
    private final function __clone() {}
 
    public static function getInstance()
    {
        if (self::$uniqueInstance === NULL ||
            self::$testing) {
            self::$uniqueInstance = new Singleton;
        }
 
        return self::$uniqueInstance;
    }
}

Выставив Singleton::$testing = TRUE; мы гарантируем, что метод getInstance() будет создавать новый объект при каждом вызове.

Выдержка из публикации Sebastian Bergmann

четверг, 14 октября 2010 г.

Javascript. PURE. Фильтрация и сортировка в директивах с итерациями.

При проходе по коллекции объектов можно фильтровать или сортировать значения.
См. комментарии в коде примера.
...

<!-- HTML шаблон -->
<ul>

<li></li>
</ul>

<script>
var data = {
legs:4,
animals:[
{name:'dog', legs:4},
{name:'cat', legs:4},
{name:'bird', legs:2},
{name:'mouse', legs:4}
]
};

// Директивы шаблонизатора
var directive = {
'li':{
'animal<-animals':{
'.':'animal.name'
},
sort:function(a, b){
return a.name > b.name ? 1 : -1;
},
// Текущий объект на каждой итерации доступен
// через свойство item параметра функции.
// Функция должна возвращать Boolean (true, false).
// Если функция возвращает false, объект пропускается.
filter:function(a){
return a.context.legs === a.item.legs;
}
}
};

$('ul').render(data, directive);
</script>

...

Javascript. PURE. Добавление строки в список.

Что делать, если хотим хотим добавить новую строку в список, но не хотим при этом его заново генерировать?
На самом деле эту задачу можно решить разными способами, но я выбрал следующий.
Имеем следующие данные и директивы:
// Объект
data = { value: 'myVal' };
// Вставить в ячейку таблицы значение объекта
// !!! В PURE мы можем использовать селекторы CSS для доступа к элементам !!!
// Точно также, как в Jquery и это очень хорошая новость
var directive = { 'td': 'value' };
Создаем шаблон-таблицу (список):
<table>
<tr>
<td></td>
</tr>
</table>

Выбираем tr как фрагмент шаблона, который хотим использовать.
Используем метод compile для преобразования шаблона в функцию.
// Как параметр foo будет принимать данные для вставки в шаблон
// а возвращать уже готовый html
var foo = $('tr').compile(directive);
// !!! Jquery !!!
// Добавляем новую строку в таблицу
$('table').append(foo(data))

среда, 13 октября 2010 г.

Jquery. Событие у элемента добавленного в DOM после загрузки страницы.

.live() позволяет прикрепить обработчик события к элементу с заданным селектором в любом случае, был ли он в DOM при загрузке страницы или будет добавлен в будущем

$('.clickme').live('click', function() {
// Обработчик
});

Javascript. Ультра быстрый шаблонизатор PURE.

С помощью этого инструмента логика и представление будут полностью разделены.
Работает как сам, так и в связке с популярными javascript фреймворками:
dojo, DomAssistant, jQuery, Mootools, Prototype, Sizzle и Sly

PURE

Создаем страницу html:

<html>
<head />
<body />
</html>
Загружаем javascript библиотеки:
<html>
<head>

<script src="jquery.js"></script>
<script src="pure.js"></script>

</head>
<body />
</html>
Создаем шаблон html:
<html>
<head>
<script src="jquery.js"></script>
<script src="pure.js"></script>
</head>
<body>

<div class="who"></div>

</body>
</html>
Получаем данные:
<html>
<head>
<script src="jquery.js"></script>
<script src="pure.js"></script>
</head>
<body>
<div class="who"></div>

<script>
var data = {who:'Hello Wrrrld!'};
</script>

</body>
</html>
Запускаем PURE для преобразования JSON в HTML:
<html>
<head>
<script src="jquery.js"></script>
<script src="pure.js"></script>
</head>
<body>
<div class="who"></div>
<script>
var data = {who:'Hello Wrrrld!'};

$('div.who').autoRender(data);

</script>
</body>
</html>
Получаем результат:
<!-- шаблон -->
<div class="who"></div>
<!-- результат -->
<div class="who">Hello Wrrrld!</div>

Jquery. Плагин для работы с JSON.

Кодирование и декодирование JSON в javascript.
Страница проекта на code.google.com

Использование:
var thing = {plugin: 'jquery-json', version: 2.2};
var encoded = $.toJSON(thing); //'{"plugin":"jquery-json","version":2.2}'
var name = $.evalJSON(encoded).plugin; //"jquery-json"
var version = $.evalJSON(encoded).version; // 2.2

Zend Framework. Массив элементов формы.

Возникла задача создать массив элементов формы с именем вида myName[].

Вот такое:
<input type='hidden' name='items[]' value='1' />
<input type='hidden' name='items[]' value='2' />
<input type='hidden' name='items[]' value='3' />
На StackOverflow описывалось решение и оно на первый взгляд неплохое.
// Вариант 1
$form = new Zend_Form();
$subForm = Zend_Form_SubForm();
$subForm->addElement('Text', '1')
  ->addElement('Text', '2');
$form->addSubForm($subForm, 'element');

// Вариант 2
$form = new Zend_Form();
$form->addElement('Text', '1', array('belongsTo' => 'element'))
  ->addElement('Text', '2', array('belongsTo' => 'element'));
Все хорошо, но класс элемента формы Zend Framework требует уникальное имя.
Решение на скорую руку:
// Custom/Form/Element/HiddenArray.php
class Custom_Form_Element_HiddenArray extends Zend_Form_Element
{
  public $helper = 'HiddenArray';
}

// Custom/View/Helper/HiddenArray.php
class Custom_View_Helper_HiddenArray extends Zend_View_Helper_FormElement
{
  public function HiddenArray($name, $value = array(), $attribs = null)
  {
    $value = (array) $value;
    $html = '';
    foreach($value as $val) {
      $html .= "<input type='hidden' name='{$name}[]' value='{$val}' />\n";
    }
    return $html;
  }
}

// ...
$elem = new Custom_Form_Element_HiddenArray('items');
$elem->setValue(array(1, 2, 3));
$form->addElement($elem);

понедельник, 11 октября 2010 г.

PHP. Как узнать имя класса в контексте статического метода.

Тут очень полезна функция get_called_class(), которая доступна начиная с версии PHP 5.3
Реализация абстрактного одиночки:

abstract class Singleton {

protected function __construct() {
}

final public static function getInstance() {
static $aoInstance = array();

$calledClassName = get_called_class();

if (! isset ($aoInstance[$calledClassName])) {
$aoInstance[$calledClassName] = new $calledClassName();
}

return $aoInstance[$calledClassName];
}

final private function __clone() {
}
}

class DatabaseConnection extends Singleton {

protected $connection;

protected function __construct() {
// @todo Connect to the database
}

public function __destruct() {
// @todo Drop the connection to the database
}
}

$oDbConn = new DatabaseConnection(); // Fatal error

$oDbConn = DatabaseConnection::getInstance(); // Returns single instance

ORM Doctrine. Слушатели событий записи (Record Listeners)

Слушатели могут быть добавлены к объектам Doctrine_Record и Doctrine_Validator.
Все методы получают как параметр объект класса Doctrine_Event.

Список доступных методов слушателей:

Методы









Слушают




preSave()








save()



postSave()








save()



preUpdate()








save() when the record state is DIRTY



postUpdate()








save() when the record state is DIRTY



preInsert()








save() when the record state is TDIRTY



postInsert()








save() when the record state is TDIRTY



preDelete()








delete()



postDelete()








delete()



preValidate()








validate()



postValidate()








validate()





Пример:

class Debugger extends Doctrine_Record_Listener {

public function postDelete(Doctrine_Event $event) {
echo 'deleted ' . $event->getInvoker()->id;
}

}

// Добавление слушателя так ...

class MyRecord extends Doctrine_Record {

// ...

public function setUp() {
$this->addListener(new Debugger());
}

}

// ... или так

$myRecord = new MyRecord();
$myRecord->addListener(new Debugger());

воскресенье, 10 октября 2010 г.

Zend Framework. Валидатор URL.

Для проверки url вида:
http://www.websites.com
http://subdomain.websites.com
http://www.websites.com/directory/
http://www.websites.com/index.php?whatever=xyz


class My_Validate_Url extends Zend_Validate_Abstract {
const INVALID_URL = 'invalidUrl';

protected $_messageTemplates = array(
self::INVALID_URL => "'%value%' is not a valid URL.",
);

public function isValid($value) {
$valueString = (string) $value;
$this->_setValue($valueString);

if (!Zend_Uri::check($value)) {
$this->_error(self::INVALID_URL);
return false;
}
return true;
}
}

$website = $form->createElement('text', 'website');
$website->addValidator(new My_Validate_Url());

Ubuntu. Восстановление загрузчика GRUB2.

1. Загружаемся с liveCD или загрузочной флешки, сделать ее можно с помощью http://unetbootin.sourceforge.net/.
2. Запускаем gnome-terminal
3. Изучаем таблицу разделов

sudo fdisk -l
видим что-то вроде этого ...
/dev/sda1 29 8369 66999082+ 83 Linux
/dev/sda2 * 8370 13995 45190845 7 HPFS/NTFS
/dev/sda3 13996 14593 4803435 5 Extended
/dev/sda5 13996 14593 4803403+ 82 Linux swap / Solaris


и монтируем наш Linux-раздел ...

sudo mount /dev/sda1 /mnt
sudo mount --bind /dev /mnt/dev
sudo mount --bind /proc /mnt/proc


4. Меняем корневой раздел

sudo chroot /mnt

chroot — операция изменения корневого каталога в Unix-подобных операционных системах. Программа, запущенная с изменённым корневым каталогом, будет иметь доступ только к файлам, содержащимся в данном каталоге. Поэтому, если нужно обеспечить программе доступ к другим каталогам или файловым системам (например, /proc), нужно заранее примонтировать в целевом каталоге необходимые каталоги или устройства.

После этого sudo больше не требуется

5. Устанавливаем grub
grub-install /dev/sda

6. Возвращаемся в корневой раздел liveCD отмонтируем разделы и перезагружаемся:

exit
sudo umount /mnt/dev
sudo umount /mnt/proc
sudo umount /mnt
sudo reboot


Для редактирования меню GRUB существует очень удобная утилита Grub Customizer.
Установить ее можно так:

sudo add-apt-repository ppa:danielrichter2007/grub-customizer
sudo apt-get update
sudo apt-get install grub-customizer

четверг, 7 октября 2010 г.

Zend Framework. Стандартные помощники вида. Составной модульный шаблон с Partial Helper.

Этот хелпер используется для встраивания шаблона с собственной областью видимости переменных. В основном полезно для многократного использования фрагментов шаблона, при этом можно не беспокоиться по поводу конфликтов имен. Дает возможность создавать модульные шаблоны (скрипты вида).
// partial.phtml
<ul>
<li>From: <?php echo $this->escape($this->from) ?></li>
<li>Subject: <?php echo $this->escape($this->subject) ?></li>
</ul>

// Шаблон в который встраиваем фрагмент partial.phtml
<?php echo $this->partial('partial.phtml', array(
'from' => 'Team Framework',
'subject' => 'view partials'));
?>

// Результат
<ul>
<li>From: Team Framework</li>
<li>Subject: view partials</li>
</ul>

ORM Doctrine. Наследование (Column Aggregation)

Предположим, что у нас есть таблица Entity. В нашем приложении мы вводим две сушности
User и Group, которые обе являются Entity и их данные хранятся в одной таблице. Таблица (класс) Entity имеет атрибут type, определяющий, относится объект (запись) к классу User или Group. Мы определяем, что для User type равен 1, а для Group - 2.

Все что нам нужно - это создать 3 записи и вызвать метод Doctrine_Table::setSubclasses() в родительском классе:

// models/Entity.php

class Entity extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('name', 'string', 30);
$this->hasColumn('username', 'string', 20);
$this->hasColumn('password', 'string', 16);
$this->hasColumn('created_at', 'timestamp');
$this->hasColumn('update_at', 'timestamp');

$this->setSubclasses(array(
'User' => array('type' => 1),
'Group' => array('type' => 2)
)
);
}
}

// models/User.php
class User extends Entity
{ }

// models/Group.php
class Group extends Entity
{ }

Это же в формате YAML:

---
Entity:
columns:
username: string(20)
password: string(16)
created_at: timestamp
updated_at: timestamp

User:
inheritance:
extends: Entity
type: column_aggregation
keyField: type
keyValue: 1

Group:
inheritance:
extends: Entity
type: column_aggregation
keyField: type
keyValue: 2

Если посмотреть SQL, который генерируется в этом случае, то увидим следующее для всех трех классов:

// test.php

// ...
$sql = Doctrine_Core::generateSqlFromArray(array('Entity', 'User', 'Group'));
echo $sql[0];

CREATE TABLE entity (id BIGINT AUTO_INCREMENT,
username VARCHAR(20),
password VARCHAR(16),
created_at DATETIME,
updated_at DATETIME,
type VARCHAR(255),
PRIMARY KEY(id)) ENGINE = INNODB


Ну и дальше играемся с этим как хотим, логика очевидна:

// test.php

// ...
$user = new User();
$user->name = 'Bjarte S. Karlsen';
$user->username = 'meus';
$user->password = 'rat';
$user->save();

// ...
$group = new Group();
$group->name = 'Users';
$group->username = 'users';
$group->password = 'password';
$group->save();


// ...
$q = Doctrine_Query::create()
->from('Entity e')
->where('e.id = ?');

$user = $q->fetchOne(array($user->id));

echo get_class($user); // User

// ...
$q = Doctrine_Query::create()
->from('Entity e')
->where('e.id = ?');

$group = $q->fetchOne(array($group->id));

echo get_class($group); // Group


// Мы можем делать индивидуальные выборки для каждой модели
$q = Doctrine_Query::create()
->select('u.id')
->from('User u');

echo $q->getSqlQuery();

// вызов метода $q->getSql() вернет следующий запрос
SELECT
e.id AS e__id
FROM entity e
WHERE (e.type = '1')

среда, 6 октября 2010 г.

UML. Наследование (обобщение).


Диаграмма классов, показывающая наследование двух подклассов от одного суперкласса
Обобщение (Generalization) показывает, что один из двух связанных классов (подтип) является более частной формой другого (надтипа), который называется обобщением первого. На практике это означает что любой экземпляр подтипа является также экземпляром надтипа. Например: животные — супертип млекопитающих, которые в свою очередь супертип приматов и так далее. Эта взаимосвязь легче всего описывается фразой «А — это Б» (приматы — это млекопитающие, млекопитающие — это животные).
Графически генерализация представляется линией с пустым треугольником у супертипа
Генерализация также известна как наследование или "is a" взаимосвязь.

UML. Агрегация и композиция.

Агрегация — это разновидность ассоциации, при отношении между целым и его частями. Как тип ассоциации, агрегация может быть именованной. Агрегация не может включать сразу несколько классов.
Агрегация встречается, когда один класс является коллекцией или контейнером других. Причём по умолчанию, агрегацией называют агрегацию по ссылке, т.е. когда время существования содержащихся классов не зависит от времени существования содержащего их класса. Если контейнер будет уничтожен, то его содержимое — нет.
Графически агрегация представляется пустым ромбиком на блоке класса и линией, идущей от этого ромбика к содержащемуся классу.



Композиция — более строгий вариант агрегации. Известна также как агрегация по значению.
Композиция имеет жёсткую зависимость времени существования экземпляров класса контейнера и экземпляров содержащихся классов. Если контейнер будет уничтожен, то всё его содержимое будет также уничтожено .
Графически представляется как и агрегация, но с закрашенным ромбиком.

вторник, 5 октября 2010 г.

ORM Doctrine. Параметры инструмента командной строки Doctrine_Cli.


$options = array('pearStyle' => true,
'packagesPrefix' => 'Package',
'packagesPath' => '',
'packagesFolderName' => 'packages',
'suffix' => '.php',
'generateBaseClasses' => true,
'generateTableClasses' => false,
'generateAccessors' => false,
'baseClassPrefix' => 'Base',
'baseClassesDirectory' => 'generated',
'baseClassName' => 'Doctrine_Record');

// Configure Doctrine Cli
// Normally these are arguments to the cli tasks but if they are set here the
// arguments will be auto-filled
$config = array(
'data_fixtures_path' => DATA_FIXTURES_PATH,
'models_path' => MODELS_PATH,
'migrations_path' => MIGRATIONS_PATH,
'sql_path' => SQL_PATH,
'yaml_schema_path' => YAML_SCHEMA_PATH,
'generate_models_options' => $options
);

$cli = new Doctrine_Cli($config);
$cli->run($_SERVER['argv']);

Упрощенная организация тестирования с PHPUnit и NetBeans IDE

В NetBeans IDE тестовые классы PHPUnit можно создавать в контекстном меню (вызов контекстного меню для файла в дереве проекта). Подробно это описано в документации.

Для запуска тестов проекта NetBeans использует класс NetBeansSuite, который находится в моем случае в каталоге C:\Program Files\NetBeans 6.10 M1\php\phpunit\NetBeansSuite.php. Мы можем переопределить стандартную конфигурацию запуска тестов тремя способами:

1. Файл начальной загрузки (bootstrap file)
2. Файл конфигурации xml
3. Собственный класс расширяющий PHPUnit_Framework_TestSuite

В данном случае интересует вариант 3.
В каталоге с тестами проекта я создал файл MySuite.php

// MySuite.php

require_once 'MyClassATest.php';
require_once 'MyClassBTest.php';
require_once 'MyListener.php';

class MySuite extends PHPUnit_Framework_TestSuite
{
    public function  setUp()
    {
        parent::setUp();
        echo __METHOD__ . "\n";
    }

    public function  tearDown() 
    {
        parent::tearDown();
        echo __METHOD__ . "\n";
    }

    public static function suite() 
    {
        $suite = new MySuite();
        $suite->addTestSuite('MyClassATest');
        $suite->addTestSuite('MyClassBTest');
        return $suite;
    }

    public function  run(PHPUnit_Framework_TestResult $result = NULL,
        $filter = FALSE, array $groups = array(), array $excludeGroups = array(),
        $processIsolation = FALSE) 
    {
        if($result == NULL) 
        {
            $result = new PHPUnit_Framework_TestResult();
        }

        $result->addListener(new MyListener());

        parent::run($result, $filter, $groups, $excludeGroups, $processIsolation);
    }
}


В настройках проекта необходимо указать, что используем собственный класс и путь к нему. Теперь мы можем вызывать в методах setUp и tearDown все что нам нужно.

Запуск тестового набора ALT - F6 ...

PHPUnit 3.5.0 by Sebastian Bergmann.

MySuite::setUp
MyListener::startTest
MyClassATest::setUp
.MyListener::endTest
MyListener::startTest
MyClassATest::setUp
.MyListener::endTest
MyListener::startTest
MyClassBTest::setUp
IMyListener::endTest
MyListener::startTest
MyClassBTest::setUp
IMyListener::endTest
MySuite::tearDown


Time: 1 second, Memory: 4.00Mb

OK, but incomplete or skipped tests!
Tests: 4, Assertions: 1, Incomplete: 2.

Классы модели наследуют определенный супер-класс. В моем случае это Doctrine_Record.
Для того чтобы можно было создавать тесты классов из контекстного меню (Tools -> Create PHPUnit tests), необходимо создать bootstrap.php.

// bootstrap.php

function myAutoloader($className) 
{
    if(class_exists($className) === false)
        @require_once implode('/', explode('_', $className)) . '.php';
}

define('DOCROOT', realpath(dirname(__FILE__) . '/..'));
define('DOCTRINE_PATH', DOCROOT . '/library');
define('MODELS_PATH', DOCROOT . '/application/models');

set_include_path
(
    get_include_path() .
    PATH_SEPARATOR . MODELS_PATH .
    PATH_SEPARATOR . DOCROOT . '/tests/application/models'
);

require_once DOCTRINE_PATH . '/Doctrine.php';
spl_autoload_register(array('Doctrine', 'autoload'));
spl_autoload_register(array('Doctrine', 'modelsAutoload'));
spl_autoload_register('myAutoloader');

// Модель, которая будет использоваться для генерации таблиц sqlite
Doctrine_Core::setModelsDirectory(MODELS_PATH);
Doctrine_Core::loadModels(MODELS_PATH);

Что-то вроде этого ...

В настройках проекта указываем bootstrap и указываем, что используем его для генерации классов. Вызов Skeleton Generator PHPUnit также имеет опцию bootstrap.

Таким образом мы инициализируем все автозагрузчики классов.

UTF-8 в командной строке windows

chcp 65001

Установка PHPUnit 3.5


Добавляем каналы с зависимостями
pear channel-discover pear.phpunit.de
pear channel-discover components.ez.no
pear channel-discover pear.symfony-project.com
Устанавливаем phpunit с зависимостями
pear install --alldeps phpunit/PHPUnit

Настройка xdebug в php.ini


; Для unix
zend_extension=/path/to/xdebug.so
; Для windows
zend_extension = "C:\xampp\php\ext\php_xdebug-2.1.0-5.3-vc6.dll"
xdebug.remote_enable=1
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9000

понедельник, 4 октября 2010 г.

PHPUnit. Помощник инициализации базы данных для PHPUnit (Database helper for PHPUnit)

При тестировании кода, использующего базу данных, обычно мы хотим чтобы структура базы данных не менялась для каждого теста. Таблицы должны быть пустыми, данные должны соответствовать тестам.
Конечно мы можем написать класс, расширяющий ТestCase, который сделает это автоматически при инициализации, в методе setUp, и в конце, в методе tearDown, но уже существует ряд готовых решений.
Например, в Zend Framework и т.д.

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

TestListener

 

Мы можем использовать интерфейс TestListener PHPUnit для создания класса - обработчика события, который будет работать до и после выполнения каждого теста. Так как мы используем Doctrine сброс базы данных - это очень простая задача: просто создадим в оперативной памяти SQLite базу данных и позволим Doctrine создать таблицы в ней из классов модели нашего приложения. Закрытие соединения приведет к уничтожению этой базы данных, так как она находится в памяти. Это самый простой вариант.

Теперь, как определить использует ли тестовое задание базу данных? Мы конечно же не заинтересованы в бессмысленной трате ресурсов на реинициализацию базы данных. Хорошим решением будет ввод интерфейса, который будут реализовывать наши тестовые задания и который будет проверятся слушателем.

Простая реализация

 

interface DbTest_Interface
{
}

class DbResetListener implements PHPUnit_Framework_TestListener 
{
    public function startTest(PHPUnit_Framework_Test $test) {
        if($test instanceof DbTest_Interface) {
            Doctrine_Manager::connection(new PDO('sqlite::memory:'));
            Doctrine::createTablesFromModels();
        }
    }
    
    public function endTest(PHPUnit_Framework_Test $test, $time) {
        if($test instanceof DbTest_Interface) {
            Doctrine_Manager::getInstance()
                ->closeConnection(Doctrine_Manager::connection());
        }
    }
    
    //Other methods needed for interface but nothing else
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {}
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite) {}
    public function addError(PHPUnit_Framework_Test $test,
        Exception $e, $time) {}
    public function addFailure(PHPUnit_Framework_Test $test, 
            PHPUnit_Framework_AssertionFailedError $e, $time) {}
    public function addIncompleteTest(PHPUnit_Framework_Test $test,
        Exception $e, $time) {}
    public function addSkippedTest(PHPUnit_Framework_Test $test,
        Exception $e, $time) {}
}


Все довольно просто, тестовые задания должны реализовывать интерфейс DbTest_Interface, в противном случае слушатель их проигнорирует.
Чтобы все это использовать мы должны сначала загрузить модель приложения
Doctrine::loadModels('/path/to/models');

И добавить слушатель ...
class AllTests
{
   public static function main()
   {
       $listener = new DbResetListener();
       PHPUnit_TextUI_TestRunner::run(self::suite(), array(
           'listeners' => array($listener)
       ));
   }

   /* other stuff here */
}

оригинал

воскресенье, 3 октября 2010 г.

Установка xdebug для xampp в linux.

Ниже последовательность действий по установке профайлера и отладчика для PHP.
1. Устанавливает пакет разработчика для xampp (XAMPP development package), который содержит необходимые исходники. Предварительно скачиваем его с сайта xampp.

tar xvfz xampp-linux-devel-1.7.3a.tar.gz -C /opt

2. Качаем исходники xdebug c http://www.xdebug.com/, распаковываем и переходим в соотв. директорию.
3. Запускаем phpize

leon@leon-desktop:~/xdebug-2.0.5$ /opt/lampp/bin/phpize
Configuring for:
PHP Api Version: 20090626
Zend Module Api No: 20090626
Zend Extension Api No: 220090626
Cannot find autoconf. Please check your autoconf installation and the
$PHP_AUTOCONF environment variable. Then, rerun this script.

Делаем следующее:

sudo apt-get install build-essential

И снова phpize

leon@leon-desktop:~/xdebug-2.0.5$ /opt/lampp/bin/phpize
Configuring for:
PHP Api Version: 20090626
Zend Module Api No: 20090626
Zend Extension Api No: 220090626
configure.in:3: warning: prefer named diversions
configure.in:150: warning: AC_CACHE_VAL(lt_prog_compiler_static_works, ...): suspicious cache-id, must contain _cv_ to be cached
../../lib/autoconf/general.m4:2019: AC_CACHE_VAL is expanded from...
../../lib/autoconf/general.m4:2040: AC_CACHE_CHECK is expanded from...
aclocal.m4:3555: AC_LIBTOOL_LINKER_OPTION is expanded from...
aclocal.m4:5493: _LT_AC_LANG_C_CONFIG is expanded from...
aclocal.m4:5492: AC_LIBTOOL_LANG_C_CONFIG is expanded from...
aclocal.m4:2972: AC_LIBTOOL_SETUP is expanded from...
aclocal.m4:2952: _AC_PROG_LIBTOOL is expanded from...
aclocal.m4:2915: AC_PROG_LIBTOOL is expanded from...
configure.in:150: the top level
configure.in:150: warning: AC_CACHE_VAL(lt_prog_compiler_pic_works, ...): suspicious cache-id, must contain _cv_ to be cached
aclocal.m4:3510: AC_LIBTOOL_COMPILER_OPTION is expanded from...
aclocal.m4:7620: AC_LIBTOOL_PROG_COMPILER_PIC is expanded from...
configure.in:150: warning: AC_CACHE_VAL(lt_prog_compiler_pic_works_CXX, ...): suspicious cache-id, must contain _cv_ to be cached
aclocal.m4:5606: _LT_AC_LANG_CXX_CONFIG is expanded from...
aclocal.m4:5605: AC_LIBTOOL_LANG_CXX_CONFIG is expanded from...
aclocal.m4:4641: _LT_AC_TAGCONFIG is expanded from...
configure.in:3: warning: prefer named diversions
configure.in:150: warning: AC_CACHE_VAL(lt_prog_compiler_static_works, ...): suspicious cache-id, must contain _cv_ to be cached
../../lib/autoconf/general.m4:2019: AC_CACHE_VAL is expanded from...
../../lib/autoconf/general.m4:2040: AC_CACHE_CHECK is expanded from...
aclocal.m4:3555: AC_LIBTOOL_LINKER_OPTION is expanded from...
aclocal.m4:5493: _LT_AC_LANG_C_CONFIG is expanded from...
aclocal.m4:5492: AC_LIBTOOL_LANG_C_CONFIG is expanded from...
aclocal.m4:2972: AC_LIBTOOL_SETUP is expanded from...
aclocal.m4:2952: _AC_PROG_LIBTOOL is expanded from...
aclocal.m4:2915: AC_PROG_LIBTOOL is expanded from...
configure.in:150: the top level
configure.in:150: warning: AC_CACHE_VAL(lt_prog_compiler_pic_works, ...): suspicious cache-id, must contain _cv_ to be cached
aclocal.m4:3510: AC_LIBTOOL_COMPILER_OPTION is expanded from...
aclocal.m4:7620: AC_LIBTOOL_PROG_COMPILER_PIC is expanded from...
configure.in:150: warning: AC_CACHE_VAL(lt_prog_compiler_pic_works_CXX, ...): suspicious cache-id, must contain _cv_ to be cached
aclocal.m4:5606: _LT_AC_LANG_CXX_CONFIG is expanded from...
aclocal.m4:5605: AC_LIBTOOL_LANG_CXX_CONFIG is expanded from...
aclocal.m4:4641: _LT_AC_TAGCONFIG is expanded from...

На этот раз все ок!
4. Запускаем configure

./configure --enable-xdebug --with-php-config=/opt/lampp/bin/php-config

5. Собираем make
6. Копируем модуль в директорию с расширениями php

cp modules/xdebug.so /opt/lampp/lib/php/extensions

7. Прописываем в php.ini наш отладчик

zend_extension=/opt/lampp/lib/php/extensions/xdebug.so

8. Перезапускаем сервер и смотрим в выводе phpinfo() все ли ок.

четверг, 30 сентября 2010 г.

SQL. MySQL. Удаление внешнего ключа.

InnoDB позволяет добавить ограничение внешнего ключа с помощью ALTER TABLE:
ALTER TABLE tbl_name
    ADD [CONSTRAINT [symbol]] FOREIGN KEY
    [index_name] (index_col_name, ...)
    REFERENCES tbl_name (index_col_name,...)
    [ON DELETE reference_option]
    [ON UPDATE reference_option]
InnoDB поддерживает использование ALTER TABLE для удаления внешнего ключа:
ALTER TABLE tbl_name DROP FOREIGN KEY fk_symbol;
Пример:
--
-- Ограничения внешнего ключа таблицы `playlist`
--
ALTER TABLE `playlist` ADD CONSTRAINT `playlist_user_id_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE `playlist` DROP FOREIGN KEY `playlist_user_id_user_id`

среда, 29 сентября 2010 г.

Jquery. Передача дополнительных параметров неанонимному обработчику события.


function handler(event) {
alert(event.data.foo);
}
$("p").bind("click", {foo: "bar"}, handler)


Как результат получаем 'bar'.

C++. STL. Map.


#include <iostream>
#include <vector>
#include <string>
#include <list>
#include <map>
using namespace std;

main () {
map<int, string> myMap1;
myMap1[1] = "leon bob";
myMap1[2] = "john doe";
myMap1[3] = "jane doe";
map<int, string>::iterator i;
for(i=myMap1.begin(); i!=myMap1.end(); i++) {
cout << (*i).first << ":" << (*i).second << endl;
}
cout << myMap1[1] << endl;
map<string, string> myMap2;
myMap2["leonBob"] = "developer";
cout << myMap2["leonBob"] << endl;
}


leon@leon-desktop:~/cpp$ g++ myapp.cpp
leon@leon-desktop:~/cpp$ ./a.out
1:leon bob
2:john doe
3:jane doe
leon bob
developer

понедельник, 27 сентября 2010 г.

среда, 22 сентября 2010 г.

ORM Doctrine. Реальное наследование (concrete inheritance).

При этом типе наследования ORM Doctrine создает отдельные таблицы для дочерних классов. При этом каждый класс генерирует таблицы, которые содержат все столбцы (включая и унаследованные). Для того чтобы использовать такой тип наследования, вам необходимо явно вызвать parent::setTableDefinition()
у дочерних классов, как показано ниже:


// models/TextItem.php

class TextItem extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('topic', 'string', 100);
}
}



Теперь давайте создадим модель Comment, расширяющую TextItem, и добавим новый столбец content:

// models/Comment.php

class Comment extends TextItem
{
public function setTableDefinition()
{
parent::setTableDefinition();

$this->hasColumn('content', 'string', 300);
}
}


Теперь в формате YAML:

---
# schema.yml

# ...
TextItem:
columns:
topic: string(100)

Comment:
inheritance:
extends: TextItem
type: concrete
columns:
content: string(300)


Если посмотреть SQL ...

// test.php

// ...
$sql = Doctrine_Core::generateSqlFromArray(array('TextItem', 'Comment'));
echo $sql[0] . "\n";
echo $sql[1];



CREATE TABLE text_item (id BIGINT AUTO_INCREMENT,
topic VARCHAR(100),
PRIMARY KEY(id)) ENGINE = INNODB

CREATE TABLE comment (id BIGINT AUTO_INCREMENT,
topic VARCHAR(100),
content TEXT,
PRIMARY KEY(id)) ENGINE = INNODB

Вам совсем не обязательно создавать новые столбцы у дочерних классов, в случае, если вы просто хотите хранить данные в разных таблицах.

ORM Doctrine. Простое наследование (simple inheritance).

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


// models/Entity.php

class Entity extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('name', 'string', 30);
$this->hasColumn('username', 'string', 20);
$this->hasColumn('password', 'string', 16);
$this->hasColumn('created_at', 'timestamp');
$this->hasColumn('update_at', 'timestamp');
}
}



Теперь создаем модель User, расширяющую (наследующую) Entity ...


// models/User.php

class User extends Entity
{ }


И тоже самое для класса Group ...

// models/Group.php

class Group extends Entity
{ }



Вот так это будет выглядеть в формате YAML:

---
# schema.yml

# ...
Entity:
columns:
name: string(30)
username: string(20)
password: string(16)
created_at: timestamp
updated_at: timestamp

User:
inheritance:
extends: Entity
type: simple

Group:
inheritance:
extends: Entity
type: simple


Если посмотреть на SQL, который генерируют эти модели ...

// test.php

// ...
$sql = Doctrine_Core::generateSqlFromArray(array('Entity', 'User', 'Group'));
echo $sql[0];



... то в обоих случаях будет следующее:

CREATE TABLE entity (id BIGINT AUTO_INCREMENT,
username VARCHAR(20),
password VARCHAR(16),
created_at DATETIME,
updated_at DATETIME,
name VARCHAR(30),
PRIMARY KEY(id)) ENGINE = INNODB

Jquery. Работа с массивами значений элементов input.



<script type="text/javascript">
$(document).ready(function() {

...

// Контейнер для значений элементов формы
var videoIds = [];
// Проход по объектам элементов формы с помощью итератора ...
$("input[name=videoIds\\[\\]]").each(function () {
// ... помещаем значение в контейнер
videoIds.push(this.value);
})
// Выводим результат, склеивая значения элементов массива
alert(videoIds.join(', '));

...
});
</script>

<input type="hidden" name="videoIds[]" value="1" />
<input type="hidden" name="videoIds[]" value="2" />
<input type="hidden" name="videoIds[]" value="3" />


В результате мы должны увидеть следующее
1, 2, 3

понедельник, 13 сентября 2010 г.

PHP. Функция set_include_path().

Действие этой функции оказывает влияние только на include и require.
Такие функции, как is_file, is_readable ничего об этом не знают ;-)

воскресенье, 12 сентября 2010 г.

Table Data Gateway (Шлюз к данным таблицы).

Table Data Gateway (Шлюз к данным таблицы).








Объект выступает в качестве шлюза между данными в приложении и в БД. Один объект работает сразу со всеми записями в таблице.

Сочетание SQL-запросов и логики приложения может вызвать достаточно много проблем. Есть разработчики, которые не сильны в SQL, а есть и те, кто в этом преуспел. Администраторы SQL-серверов должны иметь возможность быстро найти SQL-код, чтобы понимать каким образом настраивать и развивать сервера.

Объект шлюза к таблице содержит все запросы SQL для доступа к отдельной таблице или представлению (view): выборка, обновление, вставка, удаление (CRUD). Остальной код, для взаимодействия с БД, обращается к методам объекта шлюза.

Пример: объект шлюза PersonGateway содержит методы для доступа к таблице person в БД. Методы содержат SQL-код для выборки, вставки, обновления и удаления. Объект может содержать специальную выборку, например поиск по компании.

Источник

четверг, 9 сентября 2010 г.

PHP. Шаблоны проектирования. Адаптер.

Шаблон проектирования "Адаптер" используется, когда требуется преобразовать объект одного типа в объект другого типа. Использование этого шаблона, как и всех прочих, повышает чистоту кода и возможность его многократного использования.


// Класс для вывода адреса
class AddressDisplay
{
private $addressType;
private $addressText;

public function setAddressType($addressType)
{
$this->addressType = $addressType;
}

public function getAddressType()
{
return $this->addressType;
}

public function setAddressText($addressText)
{
$this->addressText = $addressText;
}

public function getAddressText()
{
return $this->addressText;
}
}

// Класс адреса электронной почты
class EmailAddress
{
private $emailAddress;

public function getEmailAddress()
{
return $this->emailAddress;
}

public function setEmailAddress($address)
{
$this->emailAddress = $address;
}
}

// Адаптер
class EmailAddressDisplayAdapter extends AddressDisplay
{
public function __construct($emailAddr)
{
$this->setAddressType("email");
$this->setAddressText($emailAddr->getEmailAddress());
}
}

// Использование адаптера
$email = new EmailAddress();
$email->setEmailAddress("user@example.com");

$address = new EmailAddressDisplayAdapter($email);

echo($address->getAddressType() . "\n") ;
echo($address->getAddressText());


Как видно, после реализации шаблона, нам больше нечего беспокоится за то, как EmailAddress преображается в AddressDisplay. И это хорошая новость, особенно если изменится реализация AddressDisplay.
Основное преимущество модульного дизайна в том, чтобы иметь возможность менять минимум кода при изменении бизнес-модели приложения или добавлении нового функционала. Важно помнить об этом даже в тривиальных вещах, таких как передача значений от объекта к объекту.

C++. STL. Константный итератор (constant iterator) и реверсивный итератор (reverse Iterator).

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

... если в контейнере итератор ссылается на первый элемент данных, то реверсивный итератор ссылается на последний ...

vector myvec;
myvec.push_back("Hello, ");
myvec.push_back("kitty!");
vector::const_iterator ci;
for(ci=myvec.begin(); ci!=myvec.end(); ci++) {
print(*ci);
}

// Hello,
// kitty!

vector::reverse_iterator ri;
for(ri=myvec.rbegin(); ri!=myvec.rend(); ++ri) {
print(*ri);
}
// kitty!
// Hello,

PHP. Запуск одиночного тестового класса PHPUnit.


phpunit MyTest path/to/MyTest.php

C++. STL. Контейнер vector.

файл myapp.cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;

void print(string str) {
cout << str << endl;
}

main()
{
vector<string> myvec;
myvec.push_back("my string 1");
myvec.push_back("my string 2");
myvec.push_back("my string 3");

int i;
for(i=0; i<myvec.size(); i++) {
print(myvec[i]);
}
}


g++ myapp.cpp -o myapp
./myapp

my string 1
my string 2
my string 3

среда, 8 сентября 2010 г.

PHP. PDO. Узнать структуру таблицы MySQL.

Как узнать название полей таблицы в базе данных (ее структуру), используя PDO???
$dbh = new PDO('mysql:dbname=mydb;host=myhost', 'login', 'pwd');
$sth = $dbh->prepare('SHOW COLUMNS FROM mytable');
$sth->setFetchMode(PDO_FETCH_ASSOC);
$sth->execute();
while($row = $sth->fetch()) {
    print_r($row);
}
$sth = null;

PHP. PDO.

$dbh = new PDO('mysql:dbname=test;host=localhost', $username, $password);

// let's have exceptions instead of silence.
// other modes: PDO_ERRMODE_SILENT (default - check $stmt->errorCode()
// and $stmt->errorInfo()) PDO_ERRMODE_WARNING (php warnings)
$dbh->setAttribute(PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION);

// one-shot query
$dbh->exec("create table test(name varchar(255) not null primary key,
                  value varchar(255));");

// insert some data using a prepared statement
$stmt = $dbh->prepare("insert into test (name, value) values (:name, :value)");
// bind php variables to the named placeholders in the query
// they are both strings that will not be more than 64 chars long
$stmt->bindParam(':name', $name, PDO_PARAM_STR, 64);
$stmt->bindParam(':value', $value, PDO_PARAM_STR, 64);

// insert a record
$name = 'Foo';
$value = 'Bar';
$stmt->execute();

// and another
$name = 'Fu';
$value = 'Ba';
$stmt->execute();

// more if you like, but we're done
$stmt = null;


// get some data out based on user input
$what = $_GET['what'];
$stmt = $dbh->prepare('select name, value from test where name=:what');
$stmt->bindParam('what', $what);
$stmt->execute();

// get the row using PDO_FETCH_BOTH (default if not specified as parameter)
// other modes: PDO_FETCH_NUM, PDO_FETCH_ASSOC,
// PDO_FETCH_OBJ, PDO_FETCH_LAZY, PDO_FETCH_BOUND
$row = $stmt->fetch();
print_r($row);
$stmt = null;


// get all data row by row
$stmt = $dbh->prepare('select name, value from test');
$stmt->execute();
while ($row = $stmt->fetch(PDO_FETCH_ASSOC)) {
    print_r($row);
}
$stmt = null;


// get data row by row using bound ouput columns
$stmt = $dbh->prepare('select name, value from test');
$stmt->execute();
$stmt->bindColumn('name', $name);
$stmt->bindColumn('value', $value)
while ($stmt->fetch(PDO_FETCH_BOUND)) {
    echo "name=$name, value=$value\\n";
}

понедельник, 6 сентября 2010 г.

PHP. basename и pathinfo с кодировкой UTF.

Такие функции как basename, pathinfo в PHP не работают с именами файлов в кодировке UTF и это очень фигово, если не использовать такую функцию:

function pathinfo_utf($path) {

    if (strpos($path, '/') !== false) 
        $basename = end(explode('/', $path));
    elseif (strpos($path, '\\') !== false)
        $basename = end(explode('\\', $path));
    else 
        return false;

    if (!$basename)
        return false;

    $dirname = substr($path, 0,
                   strlen($path) - strlen($basename) - 1);

    if (strpos($basename, '.') !== false) {
        $extension = end(explode('.', $path));
        $filename = substr($basename, 0,
                        strlen($basename) - strlen($extension) - 1);
    } else {
        $extension = '';
        $filename = $basename;
    }

    return array (
            'dirname' => $dirname,
            'basename' => $basename,
            'extension' => $extension,
            'filename' => $filename
    );
}

Спасибо jjoss at mail dot ru, который опубликовал ее на php.net, кажется она работает ;-)

суббота, 4 сентября 2010 г.

Ubuntu. На нетбуке.

Если вы счастливый обладатель нетбука Samsung и решили установить Ubuntu Netbook Remix (в моем случае это Samsung n150 с предустановленной Windows 7) и если у вас что-то вдруг не работает из коробки, например, подсветка экрана при работе от аккумулятора и fn-keys регулирования уровня подсветки, как у меня, то вам необходимо установить полезные пакеты из следующего репозитория

https://launchpad.net/~voria/+archive/ppa

sudo apt-add-repository ppa:voria
sudo apt-get update && sudo apt-get upgrade
sudo apt-get install samsung-tools samsung-backlight


После установки samsung-backlight и samsung-tools все мои мечты сбылись, чего и вам желаю!!!

пятница, 27 августа 2010 г.

Scheme. Lisp. Заметки.


; Вычисление модуля

(define (abs x)
(cond ((< x 0) (- x))
(else x)))

(define (abs x)
(if (< x 0) ; Предикат
(- x) ; Следствие
x)) ; Альтернатива

четверг, 26 августа 2010 г.

.htaccess и кодировка html-файлов UTF-8

Если ничего не помогает, и браузер не хочет выставлять кодировку по-умолчнанию
(DOCTYPE, meta - все ок), можно попробовать выставить в .htaccess:

AddDefaultCharset UTF-8

PHP. Использование SimpleXML.



Код:

$newsXML = new SimpleXMLElement("<news></news>");
$newsXML->addAttribute('newsPagePrefix', 'value goes here');
$newsIntro = $newsXML->addChild('content');
$newsIntro->addAttribute('type', 'latest');
$newsXML->content = 'hello!';
Header('Content-type: text/xml');
echo $newsXML->asXML();


Результат:

<?xml version="1.0"?>
<news newsPagePrefix="value goes here"><content type="latest">hello!</content></news>

понедельник, 23 августа 2010 г.

Zend Framework. Создание собственного элемента формы Zend_Form

Версия ZF: 1.10.
Создание собственного элемента формы Zend_Form довольно тривиально.
Ниже пример для класса Securimage. Собственно в Zend Framework достаточно вариантов капчи на любой вкус, все это лишь для иллюстрации того, как это работает.
Где-то у нас есть объект Zend_View ... Надо указать путь к хелперам (view helpers).
Допустим они у нас в каталоге Custom (по умолчанию Zend_View смотрит Zend/View/Helper/).

$view = new Zend_View();
$view->setHelperPath('Custom/View/Helper/', 'Custom_View_Helper_');
Объект вида можно хранить, допустим в реестре, а вообще как хотим.

файл: Custom/Form/Element/Securimage.php

class Custom_Form_Element_Securimage extends Zend_Form_Element
{
public $helper = 'Securimage';
}
Собственно и все, просто указали хелпер вида для отображения, в данном случае.
Все может быть гораздо сложнее, но нам тут это не нужно.

файл: Custom/View/Helper/Securimage.php
class Custom_View_Helper_Securimage extends Zend_View_Helper_FormElement
{
public function securimage($name, $value = null, $attribs = null)
{
$html = "<img src='http://path/to/captcha' />";
$html .= "<input type='text' name='{$name}' />";
return $html;
}
}
?>
Одноименный метод хелпера возвращает html-код нашего элемента.
Это то что мы увидим в результате вызова метода render нашего элемента.

Ну и наконец нам нужен валидатор значения нашего элемента.
Подключение класса Securimage обеспечено автозагрузчиком.
Валидатор наследует класс фреймворка Zend_Validate_Abstract и должен реализовать метод
isValid с определенным списком параметров (это важно, иначе он не будет вызван методом isValid класса Zend_Form).

файл: Custom/Validate/Securimage.php
class Custom_Validate_Securimage extends Zend_Validate_Abstract
{
const INVALID = 'invalid';

protected $_messageTemplates = array(
self::INVALID => 'Неверный код'
);

public function isValid($val, $context = null)
{
$val = (string) $val;
$this->_setValue($val);
$si = new Securimage();
if(!$si->check($val)) {
$this->_error(self::INVALID);
return false;
}
return true;
}
}
Осталось совсем немного, создать форму и добавить туда наш элемент, примерно так ...
class MyForm extends Zend_Form
{
pulic function init()
{
// ....

$si = new Custom_Form_Element_Securimage('cap');
$si->addValidator(new Custom_Validate_Securimage());
$si->setRequired();
$si->setLabel('Код');
$this->addElement($si);

// ....
}
}
Пути к файлам элемента и валидатора указывать не требуется, все включается автоматически.
Если нет, то возможно потребуется явно подключить все необходимые файлы.

суббота, 21 августа 2010 г.

понедельник, 16 августа 2010 г.

Zend Framework. Собственный Zend_Filter.

Создание собственного фильтра для Zend_Form, реализуем интерфейс Zend_Filter_Interface:

class MyFilter implements Zend_Filter_Interface
{
public function filter($value)
{
return $valueFiltered;
}
}

// Использование
$filterChain = new Zend_Filter();
$filterChain->addFilter(new MyFilter());

суббота, 14 августа 2010 г.

Perl. Обработка исключений.

Вот такой простой способ обработки исключений в Perl

eval { # try
... тут наш код ...
};
if( $@ ) { # catch
... тут обработка исключений $@ ...
}

пятница, 13 августа 2010 г.

PHP. Загрузка изображения.

Два простых способа загрузить изображение

$remote_img = 'http://www.somwhere.com/images/image.jpg';
$img = imagecreatefromjpeg($remote_img);
$path = 'images/';
imagejpeg($img, $path);

// CURL
function save_image($img,$fullpath)
{
$ch = curl_init ($img);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_BINARYTRANSFER,1);
$rawdata=curl_exec($ch);
curl_close ($ch);
if(file_exists($fullpath)){
unlink($fullpath);
}
$fp = fopen($fullpath,'x');
fwrite($fp, $rawdata);
fclose($fp);
}

четверг, 12 августа 2010 г.

Perl с поддержкой threads. XAMPP. Ubuntu.

Perl в пакете XAMPP для Linux к сожалению собран без поддержки threads.
Можно пользоваться perl из системы. Для работы с MySQL через DBI при подключении указываем сокет MySQL из XAMPP.


my $dbh = DBI->connect("DBI:mysql:database=$db_name;mysql_socket=/opt/lampp/var/mysql/mysql.sock",
$db_user, $db_pwd);

среда, 11 августа 2010 г.

Ubuntu. Apache. mod_rewrite.

Подключаем модуль mod_rewrite apache:
leon@leon-desktop:/opt/lampp/htdocs/allmuz/application/scripts$ sudo a2enmod rewrite

Редактируем конфигурацию хостов:
leon@leon-desktop:/opt/lampp/htdocs/allmuz/application/scripts$ sudo nano /etc/apache2/sites-available/default

А именно, изменяем параметр AllowOverride None на AllowOverride All

Перезапускаем apache:
leon@leon-desktop:/opt/lampp/htdocs/allmuz/application/scripts$ sudo /etc/init.d/apache2 restart