Изменение размера изображения без искажений.
Свойство clip позволяет задать размеры абсолютно позиционированного элемента img.
Элемент должен быть видимым (visible).
Если используется 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, [...]);
...
}
Шаблон 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();
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
--disable-decimal-float, --disable-threads, --disable-libmudflap,
--disable-libssp, --disable-libgomp
Отключаем поддержку функций в которых нет необходимости.
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/'`
Я собираю на 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 компилятора хост-системы.
Наша задача создать абсолютно независимое от хост-системы окружение, для этого создаем .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
Переменная указывает путь к каталогу к которому монтируется раздел будущей системы.
# 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)
Для начала посмотрим реализацию шаблона 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() будет создавать новый объект при каждом вызове.
Что делать, если хотим хотим добавить новую строку в список, но не хотим при этом его заново генерировать? На самом деле эту задачу можно решить разными способами, но я выбрал следующий. Имеем следующие данные и директивы:
// Объект 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))
.live() позволяет прикрепить обработчик события к элементу с заданным селектором в любом случае, был ли он в DOM при загрузке страницы или будет добавлен в будущем
С помощью этого инструмента логика и представление будут полностью разделены. Работает как сам, так и в связке с популярными javascript фреймворками: dojo, DomAssistant, jQuery, Mootools, Prototype, Sizzle и Sly
Кодирование и декодирование 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
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
Этот хелпер используется для встраивания шаблона с собственной областью видимости переменных. В основном полезно для многократного использования фрагментов шаблона, при этом можно не беспокоиться по поводу конфликтов имен. Дает возможность создавать модульные шаблоны (скрипты вида).
Предположим, что у нас есть таблица 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');
Диаграмма классов, показывающая наследование двух подклассов от одного суперкласса Обобщение (Generalization) показывает, что один из двух связанных классов (подтип) является более частной формой другого (надтипа), который называется обобщением первого. На практике это означает что любой экземпляр подтипа является также экземпляром надтипа. Например: животные — супертип млекопитающих, которые в свою очередь супертип приматов и так далее. Эта взаимосвязь легче всего описывается фразой «А — это Б» (приматы — это млекопитающие, млекопитающие — это животные). Графически генерализация представляется линией с пустым треугольником у супертипа Генерализация также известна как наследование или "is a" взаимосвязь.
Агрегация — это разновидность ассоциации, при отношении между целым и его частями. Как тип ассоциации, агрегация может быть именованной. Агрегация не может включать сразу несколько классов. Агрегация встречается, когда один класс является коллекцией или контейнером других. Причём по умолчанию, агрегацией называют агрегацию по ссылке, т.е. когда время существования содержащихся классов не зависит от времени существования содержащего их класса. Если контейнер будет уничтожен, то его содержимое — нет. Графически агрегация представляется пустым ромбиком на блоке класса и линией, идущей от этого ромбика к содержащемуся классу.
Композиция — более строгий вариант агрегации. Известна также как агрегация по значению. Композиция имеет жёсткую зависимость времени существования экземпляров класса контейнера и экземпляров содержащихся классов. Если контейнер будет уничтожен, то всё его содержимое будет также уничтожено . Графически представляется как и агрегация, но с закрашенным ромбиком.
// 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']);
В 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 и указываем, что используем его для генерации классов. Вызов Skeleton Generator PHPUnit также имеет опцию bootstrap.
Таким образом мы инициализируем все автозагрузчики классов.
; Для 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
При тестировании кода, использующего базу данных, обычно мы хотим чтобы структура базы данных не менялась для каждого теста. Таблицы должны быть пустыми, данные должны соответствовать тестам.
Конечно мы можем написать класс, расширяющий Т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 */
}
Ниже последовательность действий по установке профайлера и отладчика для 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...