2

Относительные пути в svn:externals

При переезде рабочего Subversion-репозитория на другой сервер «посыпались» экстерналы. Старый svn-репозиторий был не доступен, а правила для svn:externals были указаны в виде:

foo_ext http://old-server.com/repo/foo 
bar_ext http://old-server.com/repo/bar

И естественно выпадали в ошибки.

Здесь и далее рассматривается условный репозиторий с такой структурой: корень находится в repo, а мы хотим подключить внешние папки foo и bar в папку lib:

repo\
   |-- bar\
   |-- foo\
   +-- lib\
       |-- bar_ext
       +-- foo_ext

Захотелось во избежание подобных проблем в будущем указать пути относительно. Оказалось, такая возможность в Subversion доступна, начиная с версии 1.5. При этом был изменен формат описания подключения внешних папок. Старый формат также допустим, но только при указании абсолютных путей. Предыдущий пример в новом формате должен выглядеть так:

http://new-server.com/repo/foo foo_ext 
http://new-server.com/repo/bar bar_ext

Т.е. сначала путь, потом папка.

Допустимы несколько способов указания относительных путей:

../ — относительно текущей папки:

../../foo foo_ext 
../../bar bar_ext

^/ — относительно корня репозитория:

^/foo foo_ext 
^/bar bar_ext

// — независимо от схемы сети (http/https):

//new-server.com/repo/foo foo_ext 
//new-server.com/repo/bar bar_ext

/ —относительно корня сервера:

/repo/foo foo_ext 
/repo/bar bar_ext

Что интересно, версии 1.5, описавшей новый формат указания svn:externals, уже три года. Но при этом практически все примеры и статьи в интернете используют старый формат.

0

Объявление полей классов в PHP

Давно хотел разобраться, что будет если поле не объявлено явно в классе, но используется для чтения/записи. Периодически натыкался на такие конструкции в чужом коде, считал неверным, а потому сам никогда  не использовал и ответа на вопрос «Что же все-таки будет?» не знал. Кстати, в официальном мануале ничего об этом не нашел. Буду рад, если подскажете, где просмотрел.

Итак. Поля классов могут задаваться явно с помощью ключевых слов private, protected и public, определяющих видимость поля:


class A {

    private $foo;

    protected $bar;

    public $baz;
}

Есть еще конструкция var, но это просто устаревший аналог public.

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


class A {

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

    public function setFoo($value) {
        $this->foo = $value;
    }
}

Второй способ никуда не годится:

  • Неявно определенное поле получает уровень доступ public. При явном определении — мы сразу указываем уровень доступа.
  • Если вызвать метод, читающий значение поля до его инициализации, то PHP сгенерирует Notice (при первом способе его нет). А поле будет инициализировано значением null.
  • То, что это поле есть в объекте, не получится узнать через Reflectionproperty_exists и get_class_vars. Только через isset и get_object_vars, и то только, если полю было присвоено значение.
  • С такими полями неудобно работать в редакторе — IDE их не подсказывает и вообще подсвечивает, как ошибочные. Можно описать эти поля с помощью PHPDoc тегами property, но непонятно, почему бы тогда не описать их явно как поля класса :).

В общем, не пользуйтесь никогда вторым способом. Он усложняет и запутывает код и является источником ошибок. Единственно, когда он имеет право на существование — это динамическое создание полей объекта. И даже тогда можно справиться геттерами и сеттерами (правда с потерями в производительности, при малом количестве вызовов потери незначительны).

3

Сокращенный тернарный оператор в PHP

Тернарный оператор — вещь известная и скучная:


$a = $expr1 ? $expr2 : $expr3;

Если $expr1 истинно, результатом $a станет $expr2, иначе $expr3.

А теперь об интересном! Начиная с версии PHP 5.3 можно опустить $expr2.


$a = $expr1 ?: $expr3;

Это равносильно записи:


$a = $expr1 ? $expr1 : $expr3;

Примеры:


$a = true ?: false; // true

$a = false ?: true; // true

$a = 1 ?: 2; // 1

$a = 0 ?: 2; // 2

В мануале об этом упоминается очень сухо и коротко. Ну а что еще добавить?

0

Яндекс.Субботник в Санкт-Петербурге

26 февраля в офисе Яндекса состоится первый Я.Субботник в Санкт-Петербурге. Количество мест — ограничено, поэтому спешите зарегистрироваться. Либо смотрите онлайн-трансляцию.

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

Список докладов и другие подробности о мероприятии смотрите тут.

Обновление: Регистрацию подтвердили, иду.

1

X-Forwarded-For и Opera

Браузеры фирмы Opera (десктопный в режиме Turbo, мобильные Mini и Mobile) используют прокси-сервера компании в Норвегии и других странах мира для сжатия трафика. Это очень удобно при плохой связи с Интернетом, особенно на мобильных телефонах. Но создает проблемы при попытке узнать IP-адрес подключившегося пользователя, например, для геолокации. Благо создатели браузера спешат нам на помощь. Специально для таких случаев они посылают нестандартизированный HTTP-загловок X-Forwarded-For, впервые внедренный для прокси-сервера Squid и впоследствии ставший стандартом де-факто для прокси. Формат заголовка:

X-Forwarded-For: {IP-адрес} [ , {IP-адрес} ]*

То есть X-Forwarded-For представляет собой строку IP-адресов, разделенных запятой, где адреса идут в порядке прокси-серверов от пользователя к серверам компании Opera (ну или любого прокси, поддерживающего этот заголовок).

Для определения географического положения пользователя нужно брать последний адрес из перечисленных в заголовке X-Forwarded-For — адрес, с которого пользователь пришел на сервера Opera. Первый может вполне быть внутренним прокси-сервером домашней сети и пользы от него нам никакой. Последний адрес может быть адресом другого прокси, но тут уж ничего не поделаешь.

Т.е. чтобы определить IP пользователя:

  1. смотрим заголовок X-Forwarded-For,
  2. если есть берем последний адрес в списке,
  3. если нет, то берем IP-адрес подключения.

Или как-то так на PHP:


function getUserIp() {
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $forwarded = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        return trim(array_pop($forwarded));
    }
    return $_SERVER['REMOTE_ADDR'];
}

И да, полностью полагаться на геолокацию на основе IP неправильно и всегда нужно давать пользователю возможность свое местоположение уточнить.

Кстати, X-Forwarded-For не единственный заголовок, выставляемый прокси. Интернеты сообщают еще о существовании заголовков X-REAL-IP, VIA и других. Но Opera их не использует :)

3

Кодировки при работе с DOMDocument

DomDocument — мощный инструмент работы с XML и HTML с использованием методов DOM. Главное при его использовании не запутаться в кодировках.

Конструктор DOMDocument принимает два параметра — версию и кодировку документа

$dom = new DOMDocument([ string $version [, string $encoding ]]);

А чтобы начать работать с существующим HTML-документом его надо загрузить в объект DOMDocument:

$dom->loadHTML( string $source );

Проблема в том, что документ загружается совсем не в той кодировке, что указана в конструкторе. Вообще, параметры конструктора никак не влияют на загруженный документ. А используются только при методах save():

$htmlString = $dom->saveHTML ();

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

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


$dom->loadHTML('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $source);

Либо сконвертировать все символы документа в HTML-entities перед импортом


$source = mb_convert_encoding($source, 'HTML-ENTITIES', 'utf-8');
$dom->loadHTML($source);

Первый вариант кажется предпочтительнее в плане производительности. В плане результатов работы различий замечено не было.

Если работать с DOM планируется много, то удобно оформить эти конструкции в отдельный класс наследника DOMDocument, добавить в методе загрузки дерева второй параметр кодировки и определить метод, например, так:


class MyDOMDocument extends DOMDocument {
    public function loadHTML($source, $encoding) {
        $source = '<meta http-equiv="Content-Type" content="text/html; charset='.
            $encoding.'">' . $source
        $dom->loadHTML('' . $source);
    }
}
0

Показываем картинки в статистике AWStats

AWStats — одна их популярных серверных систем учета статистики, работающая по принципу анализа логов доступа (access-log) Apache. Система хорошая, но часто попадалась мне недонастроенной: не показываются картинки. Картинки для AWStats, чтобы не копировать их для каждого домена, прописываются алиасами в файле конфигурации, который подключается к апачу. А не показываются потому что пути к реальным картинкам указаны неверные. Задача простая — найти иконки, найти конфиг, указать в нем правильный путь, перезапустить апач. У меня на Debian пути были такие:

Путь к иконкам:

/usr/share/awstats/icon

Конфиг апача:

/etc/apache2/httpd.conf

Соответственно новая строка в конфиге выглядит так:

Alias /icon /usr/share/awstats/icon

Ну и не забываем сделать рестарт апача. Готово!

0

Правим размеры элементов средствами для разработчика

В процессе дебага верстки наткнулся на классную фичу в браузерных средствах для разработчика. В них всех есть вкладка, показывающая выбранный элемент и его размеры и отступы. В Firebug и IE Developer Tools это вкладка Layout, в Safari и Chrome Web Inspector’е — вкладка Metrics, в Opera Dragonfly — Разметка (в русской версии).

Так вот оказывается Firebug, IE Developer Tools и Web Inspector не только показывают метрики выбранного элемента, но и позволяют править! А Firebug-умничка еще и умеет это делать стрелочками вверх-вниз. В IE и FF достаточно просто кликнуть на размер, в Web Inspector кликнуть дважды. Ну а в Dragonfly можно кликать бесконечно :) Не поможет — править метрики там нельзя. Или я просто не разобрался как. Буду рад, если подскажете.

В IE новые размеры нужно обязательно вводить с размерами, иначе они сбрасываются в ноль. В остальных средствах для разработчика, если вводить цифры без размера, то по умолчанию считается, что размер указан в пикселах. Если же указывать с размерами, то все ведут себя по разному. Firebug не позволяет указывать единицы измерения. Web Inspector всё переводит в пикселы. IE Dev Tools, как уже было указано выше, вообще без единиц размеры не принимает, показывает в тех единицах, которые были указаны, пересчитывает в пиксели только ширину и высоту элемента.

Точно также все ведут себя и с размерами, указаными через стили. То есть все (в том числе Opera Dragonfly) показывают размеры в пикселах, а IE — в указанных единицах (кроме ширины и высоты).

2

Первый коммит в yiiext

Сделал свой первый в жизни коммит в общественный проект — добавил функционала в библиотеку расширений yiiext отличного PHP-фреймворка yii.  Доволен как слон :)

0

User-agent в Internet Explorer 9

Майкрософт рассказал о строке агента в девятом Эксплорере. Вот так она будет выглядеть:

Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)

Как видно, версию движка Trident подняли до 5. Именно в таком коротком виде user-agent будет уходить на сервер. Строка агента с дополнительными данными, добавляемыми сторонними программами, будет отдаваться только через navigator.userAgent.

В режиме совместимости строка будет выглядеть так:

4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/5.0)

Для сравнения описание строки агента в 8-ой версии браузера.

Copyright © 2016 — dec5e | Site design by Trevor Fitzgerald