Перейти к основному содержимому

Профилирование PHP проектов

· 5 мин. чтения

Профилирование это анализ потребления ресурсов при работе программы. Этимология слова видимо связана с тем что профиль это некая граница, отсюда - поиск границ компонентов ПО в ресурсном пространстве. В моём случае программа это исполняемые php-скрипты, а ресурсы это время, память и нагрузка на процессор. Время исполнения не всегда связано с нагрузкой процессора. Процесс может ждать IO-ответа от более медленных источников, а может и просто спать.

Я писал про микрооптимизацию в php. Как правило отмечали критики, в общем случае она не нужна. Профилирование это взгляд с высоты птичьего полёта на подключаемый код. Он делается только на рабочей машинке, а не на production. В некоторых LAMP-сборках инструменты для этого уже установлены.

XDebug

XDebug - это php модуль, я уже давно с ним работаю как с дебаггером.

zend_extension = xdebug.so
xdebug.profiler_enable = 1
xdebug.profiler_aggregate = On
xdebug.profiler_output_dir = /tmp

Он позволяет генерировать доклад о профилировании в форме файла. Из-за глубокой иерархичности, доклад обычно весит несколько мегабайт. Настройки модуля хранятся в php.ini, там же можно и указать путь к результату профилирования. Назовём его cachegrind.out - он совместим с более общим форматом valgrind

Можно сделать так что-бы он генерировался постоянно, можно - с помощью куки-триггеров. Файл можно делать на каждый отдельный системный процесс, на отдельный URL запрос, по времени исполнения или их комбинаций. Дальше его стоит связать с одним из анализаторов:

  1. PHPStorm (Tools - Analyze stacktrace)

  2. webgrind

  3. WinCacheGrind

  4. KCacheGrind 

XHProf

Screen Shot 2012-07-06 at 11.31.19.png

XHProf - пакет PECL, написан в Facebook, поставляется тоже как модуль (xhprof.so или xhprof.dll). Позволяет учитывать значительно больше параметров, в т.ч. нагрузку на CPU и оперативную память. Позволяет сравнивать повторные запуски и агрегировать их результаты. Наиболее полезная фича - суммарный анализ по времени в виде весового графа. Граф генерируется с помощью graphviz и утилитки dot. У меня были небольшие проблемы под маком, пришлось поставить вручную.

pecl install xhprof-0.9.2

Редактируем php.ini:

 extension=xhprof.so
xhprof.output_dir=/var/tmp/xhprof

Поскольку просмотр результатов xhprof целиком web-based, то надо залинковать где-то в корне htdocs путь к xhprof_html папке. Для подключения, необходимо изменять свои скрипты. Т.е. где-то в начале своего index.php ставится include, инициализация и что-бы не бояться за какой-то exit, хук на register_shutdown_function. Вместо того что-бы инклудить каждый раз, можно воспользоваться php-директивой auto_prepend_file и включать это файл всегда

//результат будет виден с ?xhprof=1
if (extension_loaded('xhprof') && $_GET['xhprof']) {
include_once 'xhprof_lib/utils/xhprof_lib.php';
include_once 'xhprof_lib/utils/xhprof_runs.php';
xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);

function end_xhprof(){
$profiler_namespace = 'myapp'; // namespace вашего приложения
$xhprof_data = xhprof_disable();
$xhprof_runs = new XHProfRuns_Default();
$run_id = $xhprof_runs->save_run($xhprof_data, $profiler_namespace);

//Замените относительный путь к залинкованной папке
$profiler_url = sprintf('http://' . $_SERVER['SERVER_NAME'] . '/xhprof/index.php?run=%s&source=%s', $run_id, $profiler_namespace);

echo 'XHProfiler output';
}

register_shutdown_function('end_xhprof');
}

Можно конечно не делать echo ссылки, а прятать это в javascript-консольку для firebug'а, более подробно советую почитать статью Игоря Бровченко, он даже сравнивал несколько php-фреймворков.. или Лорензо Албертона

Слежка

Профайлеры конечно хороши когда есть начало, конец и результат в виде доклада. Но когда php висит как демон очень долго и хочется понять где же это он тормозит.. В утилитках типа phpcs для вывода деталей работы, часто существует флаг -v (verbose mode). Но до того как я это узнал, мой процесс уже час как висел в jenkins'е и я незнал, стоит ли мне убивать его, или он что-то полезное таки делает. К счастью, в общем случае, можно следить за IO-действиями любого процесса без его согласия с помощью strace:

strace -p IDпроцесса strace php index.php #запустит трейс вместе за запуском php в CLI режиме 

Дебаг зависимостей

PHPStorm умеет искать использование методов и классов, но это статический анализ, а вот для того что-бы динамически построить дерево зависимостей, есть отличный pecl модуль inclued (на момент установки у меня - 0.1.3)

pecl install inclued

Теперь так же редактируем php.ini:

extension=inclued.so
inclued.enabled=1
inclued.dumpdir=/tmp/inclued

Это нам даёт при каждом запросе файл с сериализованным выводом функции inclued_get_data(), которая возвращает все подключённые файлы.

Если следовать мануалу, то по идее так можно сгенерировать png-файлик..

php /usr/lib/php/pear/gengraph.php -i inclued.06567.1
dot -Tpng -o inclued.png inclued.out.dot

..но у меня это просто белый квадратик. Я попробовал другой gengraph.php из репозитория Йориса Бертелота

git clone https://github.com/eexit/Inclued.git /Users/artjom/PhpstormProjects/inclued/ 
sudo php /Users/artjom/PhpstormProjects/inclued/Inclued/gengraph.php -i inclued.07576.1
dot -Tpng -o inclued.png inclued.out.dot
open inclued.png

Увы он оказался малость битым, работал только режим зависимости классов, а не файлов, к тому же только в горизонтальном виде. Пришлось немного попатчить, в частности у dot есть режим rankdir = "LR"; с помощью которого сгенерилась красота на 6 мб.. ужас что так много инклудится, но красиво что так это обнаруживается. 

Это кстати результат загрузки админ-странички у opensource CRM'ки zurmo-stable-0.6.90, в основном там не столько Yii сложен, сколько незамороженный Readbean..

Как оказалось чуть позже, граф зависимостей файлов (режим php gengraph.php -T includes) не работал из-за конфликта с XDebug.

Gephi

Если вам как и мне не нравится что XHProf и Inclued полагаются на graphviz и dot для визуализации, то можно воспользоваться более интересным инструментом - Gephi. Тут надо смотреть как вы хотите это анализировать - можно брать наполовину собираемый .dot файл и импортировать его, а поскольку Gephi не умеет кушать json, то как вариант - парсить исходные данные и генерировать что-то типа CSV

Inclued уже сам генерирует inclued.out.dot в указанной папке, поэтому можно использовать его сразу. На выходе имеем..

Для того что-бы нарисовать картинку XHProf, надо чуть изменить файл callgraph_utils.php, в частности функцию xhprof_get_content_by_run. Она передаёт сгенерированный dot-скрипт напрямую в dot утилиту для генерации картинки. Поэтому просто сохраним его на диск для Gephi.. 

$dotfile = '/tmp/'.time().'.dot';
touch($dotfile);
$fp = fopen($dotfile,'w+');
fwrite($fp,$script);
fclose($fp);

С этим несколько проблем. Первая - это полуфабрикат - данные для dot уже агрегированные, есть только label, куда засунули и время, название метода, число вызовов, проценты и тп. Вторая - файл генерируется только когда открыть генерацию графа в png-формате в браузере

Tribes ascend

· 5 мин. чтения

Tribes: Ascend это бесплатная сетевая стрелялка от HiRez studios на движке от Unreal с моделью платной прокачки, подобно World of Tanks и League of Legends. В большинстве режимов игры участвуют две группы бойцов по 16 игроков в каждой (т.н. Blood Eagle и Diamond Sword) на одной из ~10 карт

Анализ сетей

· 6 мин. чтения

Графы это математическая формализация сетей. Поскольку я до дипломной работы перерыл много материалов, то тут буду собирать интересные доклады по этой общей теме. Эта статья составлена на основе курсов лекций Networked Life (Michael Kearns, Pennsylvania) и Social Network Analysis (Lada Adamic, Michigan) в Coursera.

Интереснейшие проблемы анализа сетей это поиск кратчайшего путивизуализация и заражение

Терминология

Наука изучения сетей междисциплинарная, поэтому и термины могут применяться разные.

ТочкиЛинииОбласть применения
Vertices (вершины)Edges, arcs (рёбра)Математика
Nodes (узлы)Links (связи)Инфотехнологии
SitesBondsФизика
ActorsTies, relationsСоциология

Программные инструменты 

GephiNetlogoiGraphPajekUCINetNodeXLNetworkXSoNIA.

Компоненты

В зависимости от того, как моделируется сеть (направленно или нет), зависят и некоторые её характеристики. Можно говорить о сильно связанном компоненте направленной сети, если из каждой его вершины можно попасть в любую другую. Большие естественные сети рано или поздно образуют гигантский компонент

Особенности естественных сетей

Кратчайший путь определяется только если сеть не имеет изолированных узлов (disconnected component). Проблема выражается в ограниченности видимости конкретного узла. Так, в социальных сетях человек ограничен знанием о своём окружении, поэтому он не видит всей картины, связывающей его с произвольным узлом остальной сети (small world effect).

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

Естественные сети (в отличие от скажем случайных сетей),  стремятся к минимизации кратчайшего пути и по мере роста числа узлов, среднее кратчайшее расстояние (диаметра сети) асимптотически уменьшается. Социальные сети на основе эмпирических исследований Траверса и Милграма (1969 г.), Лешковеца и Горвица (2008 г.) и Бэкстрома и др. (2012 г.) показывает что значение диаметра в районе 5.

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

Наконец третье свойство - длинный хвост в распределении степеней вершин (heavy tailed degree distribution, степенной закон, принцип Парето 20/80). Это проявляется в том, что в обществе есть редкие вершины, число связей у которых очень велико, а в среднем по сети "популярность" падает довольно резко и нелинейно

Заражение (перколяция, диффузия) в сетях может происходить необычайно быстро из-за пограничного свойства как структуры сети, так и свойств заражения (tipping point, threshold). Если взять случайно сгенерированный граф у которого средняя степень узла равна 4, то легко увидеть что при 100% степени заражения, порядка 90% узлов будет покрыто.

\[e=d*N/2\]

e - число рёбер
d - средняя степень узла
N - число узлов в сети

Число рёбер для ненаправленного графа

Аналогично, можно усложнять модель заражения, учитывать процент диффузии и регенерации, например для моделирования лесных пожаров с эволюционной точки зрения

Скопления

Для любой вершины можно определить степень скопления (clustering coefficient) как показатель взаимосвязанности её соседей. Если все вершины связаны, то такой подграф называется клик (clique) и имеет коэффицент 1. Теперь для расчёта коэффициента среднего скопления, достаточно просчитать коэффициент для всех вершин и взять среднее значение

\[C(u) = \frac{k+m}{k(k-1) /2 }\]

C(u) - коэффициент кластеризации вершины u
k - степень вершины
m - число связей "между друзьями"

Коэффициент кластеризации вершины

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

\[p = \frac{E}{N(N-1)/2}\]

p - плотность рёбер в графе
E - число рёбер в графе
N - число вершин

Плотность рёбер

Для естественных сетей C(u) значительно больше p

Моделирование скоплений

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

\[y \sim p + (\frac{x}{N})^a\]

y - вероятность создания новой связи между двумя вершинами
p - вероятность случайной связи
x - число общих друзей между вершинами
N - размер всей сети

alpha-модель

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

Предпочтительное связывание

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

\[p(k) = Сk^{-\alpha}\]

p(k) - вероятность вершины иметь степень k
α, C - константы

Распределение со степенным законом

Предпочтительное связывание (preferrential attachment, cumulative advantage) объясняет неравенства возникающие в сетях, в том числе почему богатые становятся богаче, почему у поп-звёзд так много поклонников. В целом это сводится к тому, что чем больше у вершины есть ресурсов, тем легче ей получить ещё - через дополнительную рекомендацию друзей, предубеждение, славу и тп.

Навигация

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

Модель Клейнберга предполагает что сеть упорядочена в матрицу k на k и каждая вершина знает свои координаты. Передача сообщения в таком случае сводится к выбору соседней вершины, наиболее близкой географически к глобальной цели. При этом можно ввести сопротивление в движении 

\[p(d) \propto (1/d)^r\]

p - вероятность новой связи между двумя вершинами
d - расстояние между вершинами
r>=0 - степень, влияющая на поведение

При очень больших r, передача сообщений происходит только локально и кратчайшие расстояния до дальних вершин могут даже не существовать.

Приуменьшении r, падает сопротивление от расстояния, но увеличивается алгоритмическая сложность поиска кратчайшего расстояния

Важные вершины

До этого всё что касалось характеристик, описывало всю сеть целиком, но единичные вершины также критично важны.

Pagerank

Один из способов оценки важности вершин в направленном графе основывается на пошаговом рекурсивном алгоритме вычисления важности вершины основываясь на важности ссылающихся на эту вершину входящих рёбрах-ссылках (out-degree). В случае если эти величины у всех вершин остаются неизменными и граф не меняется, то можно считать что сеть достигла равновесия.

\[R(p) = \sum_{q \in POINTS(p)} \frac {R(q)}{out(q)}\]

R(p) - pagerank вершины p
R(q) - pagerank вершины q которая ссылается на p (принадлежит множеству POINTS)
out(q) - число исходящих ссылок из q

page rank

Вот например как выглядит расчёт для некоторых вершин..

Рациональные процессы в сетях

Модель сегрегации Шеллинга

Шеллинг показал, что если вершины могут выступать как люди и изменять своё местоположение в матричной сети, переезжая в ближаюшую не занятую ячейку, то при случайном начальном расположении и при требовании вершины к своим соседям в 40% похожести, возникают сегрегированные области, подобно гетто-районам расовой сегрегации.

См. эмулятор.

Начальное случайное положение

Сегрегация при 40 процентах требований достигает 80 процентов похожести

Drupal 7

· 4 мин. чтения

среда, 8 августа 2012 г. в 07:20:52

Drupal 7 это конструктор сайтов (CMF). А это - его краткий обзор на основе того малого опыта что у меня был (так что это скорей статья для меня самого). Седьмая версия уже устарела, скоро готовится выйти 8 версия, частично перенявшая symfony framework

Если обобщить

Основное отличие от CMS - то что схему данных пользователь добавляет сам. Фактически вместо одной таблички БД, часто используется несколько таблиц под каждое поле что с одной стороны даёт гибкие возможности пользователю на лету создавать динамические наборы объектов, давать привилегии на него или его свойства, делать многоязычность и многоверсионность.

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

PHP-процессы при загрузке страниц довольно далеки от оптимального и могут занимать 50-70 мб, что вынуждает активно оптимизировать сайты с кешированием на Varnish и Memcache, перекидыванием поиска на Solr, включением opcode кэширования, Nginx и тп.

Теоретически Drupal поддерживает другие БД типа Mongo, но практически многие модули заточены под MySQL.

В плане frontend всё неплохо - "drupal frontend developer" может работать без хорошего IDE, потому что php-шаблоны генерируются из админки со своими названиями и префиксами в зависимости от типа блоков. Поэтому и возникают  компании, как то из местных - okiamekaiafenomen, банально легко интегрировать дизайн, когда схема тоже сделана в UI.

Терминология

Вот термины которыми оперирует система и стандартный сайт

  1. Content/Entity type концептуально это аналог классов - у них тоже есть свойства (Fields + Properties)

  2. Entities - собственно instance классов

  3. Nodes (+ Article, Users, Comments, Tags) - тоже по сути Entities, экземпляры классов

  4. Views, Regions, Panels. Динамические (могут вызывать методы)

  5. Menus - древовидная структура меню. Элементы могут быть привязаны к странице и её частям. Доступ по URL могут

  6. Modules. Собственно функционал.

  7. Themes. Аналогично модулям, только для стилей (CSS)

Структура админки

  • Content - редактирование статей и прочего содержания клиентом
  • Structure - порядок размещения блоков, изменение типов данных

Структура БД и пример запроса

Центральное место - табличка node, на которой и завязано всё содержимое. Пример запроса..

db_query("SELECT * FROM node LIMIT 5")->fetchField();
fetchObject();

Качество кода

Код - необъектное говно. Нет классов, интерфейсов, пространства имён. Сплошь и рядом используются глобальные переменные, поэтому юнит тестами покрыть его нельзя. Модули состоят из набора обычных функций со своеобразной практикой наименования, например приватные функции имеют _ префикс.  Впрочем, если сравнивать с Joomla, то архитектура более предсказуемая.

Хуки (специально названные функции) меняют данные на лету, если существует функция c похожим именем, проходя по всем модулям. Отсюда и тормоза и гейзенбаги. Например предлагая hook_foo_bar будет вызван baz_foo_bar в модуле baz. 

Есть свой формат для кода, но он не PSR, использует пробелы вместо табов. PHPStorm можно настроить с правилами Code_Sniffer скачав из модуля.

Drush

Drush это консольная утилита, облегчающая рутину, в частности очистку кэша. 

 

Ставится просто, но нуждается в связи с локальным mysql

pear channel-discover pear.drush.org 
sudo pear install drush/drush
sudo ln -s /tmp/mysql.sock /var/mysql/mysql.sock

 

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

drush dl module_name 
drush en module_name
drush cc all

 

Модули

У каждого своя папка с файлами .info и .module. По важности выделяются четыре типа, код лежит в разных местах

  1. core, optional core (/modules)

  2. contrib modules (sites/all/modules),

  3. custom modules (sites/default/modules) + VCS symlink 

Важные модули

- Views- Rules - для ограничений (форм)- Pathauto & token - для ЧПУ- Devel - для дебага- Wysiwyg / inline images- Context- Flags- Entity API- Menu Block- Menu Breadrumb  - Module filter- Chart APi- Features- Feeds- Custom formatters- SmartCrop- File Entity- IMCE- Media - CAPTCHA- OAuth- Entity tokens- Image crop- Job scheduler- Multiblock- Pathauto- Token- Google analytics- Webform- Chaos tools

Дистрибутивы

Почитать

Taxist

· 3 мин. чтения

воскресенье, 29 апреля 2012 г. в 18:13:43

Начал составлять свой рейтинг фильмов на flickchart и естественным образом столкнулся с новыми фильмами, в том числе классикой, которую ещё не видел.

"Таксист" с Робертом Де Ниро 1976 года один из них. Отличный нуар-фильм Мартина Скорсезе и мне как интроверту, любящему копаться в себе и критиковать других очень интересен. Главный герой, Трэвис - одинок и потерян. У него бессонница, депрессия и паранойя. 

Iron Lady

· 2 мин. чтения

среда, 11 апреля 2012 г. в 21:06:01

Очень тронул фильм про Маргарет Тетчер. Он не простой и не для развлечения. Он путеводный, подобно словам произносимым в первой части фильма:

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

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

Eclipse

· 5 мин. чтения

Поиграли в финскую настольную игру Eclipse, эдакую смесь Cosmic Encounter, Twilight Imperium и Endeavor. Играть могут до шести человек и в зависимости от опыта, занимает от 30 до 60 минут на человека. Мы играли впятером с двумя новичками четыре часа.

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

Cosmic Encounter

· 10 мин. чтения

Прибыла заказанная мною на амазоне настольная игра Cosmic encounter и дополнение к ней Cosmic incursion. Я играл в неё три раза, всякий раз проигрывал и тем не менее очень нравился процесс, поэтому объясню основы..

В оригинальную версию могут играть 3-5 человек, 1-3 часа. Существуют 3 дополнения, которые позволяют уместиться восьмерым игрокам за одним столом с некоторыми дополненными правилами. Игра с 1977 года пережила несколько издателей и изменений правил, что о чём-то должно говорить. У меня последняя версия от Fantasy Flight games.

Оптимальные структуры данных в PHP

· 3 мин. чтения

Как известно, родных типов данных в php немного. На самом деле в программировании, типов данных конечно же неограниченное множество, особенно когда дело касается деревьев (AAMk-d и прочая)

Стандартная библиотека php кроме интерфейсов итераторов и автоподгрузки классов предоставляет и более оптимальные типы данных.

Массивы

Что случается когда вы пытаетесь сделать универсальное и "простое" решение? Overhead с памятью. Дело в том что массивы в php на самом деле не массивы. Это сбивает немного с толку, но настоящий массив должен быть таким:

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

Именно поэтому такой код выдаст не упорядоченный по индексу текст..

$bar = array();
for ($i = 7; $i >= 0; --$i) {
$bar[$i] = $i;
}
echo implode(' ', $bar);

... а то как данные инициализировались: 7 6 5 4 3 2 1 0

Из-за того что новичка не заставляют думать о структуре данных, как то делают в Java, выходит неоптимальное использование памяти. Для поддержки таких низкоуровневых массивов есть SplFixedArray который на 25-40% быстрей массивов, а также SplStack с SplQueue. 

Структуры

Из-за своей простоты и лёгкой инициализации, массивы стали типичными контейнерами данных - их возвращает какой-нибудь адаптер БД как результат, его передают между собой методы где одним параметром не обойтись, он же итерируется или распаковывается в шаблонизаторах. 

Но я в своём движке использую объекты. Начиная с 5.4, объекты легче чем массивы. Из-за фиксации параметров классов и большей строгости, возможны подсказки при работе в IDE. Я умолчу об ООП - дополнительный методы у контейнеров могут добавить больше сложности чем решить.

Множества

Когда порядок не имеет особого значения, можно использовать множества. Конечно с массивами можно проводить такие же операции сложения (array_merge), вычитания (array_diff), пересечения (array_intersect) и проверки на присутствие (in_array), но как вы уже поняли, поиск по значению довольно медленный и вместо этого следует использовать поиск по ключу. Особенно если вы храните объекты:

$mySet = array();
$mySet(spl_object_hash($obj1)) = $obj1;
$mySet(spl_object_hash($obj2)) = $obj2;
isset($mySet(spl_object_hash($obj1))); //проверка вместо in_array
$mySet + $yourSet; //сложение работает потому что нет коллизий ключей
array_intersect_key($mySet, $yourSet); // пересечение по ключу
array_diff_key($mySet, $yourSet); // вычитание

Куча

Heap это просто такое дерево, ключи элементов которого распределяются по старшинству. Кучи часто используются в алгоритмах с графами. В php для этого есть SplHeap, SplMaxHeap и SplMinHeap, которые в сотни раз быстрей массивов.

Обобщение кучи это очередь с приоритетом - SplPriorityQueue, где порядок не FIFO/LIFO, а как вы догадались, согласно заданной важности

$priorityQueue = new SplPriorityQueue();
$priorityQueue->insert("Putin", 60);
$priorityQueue->insert("Zjuganov", 15);

Интеграционное тестирование почтовых зависимостей c Postfix

· 2 мин. чтения

Я фанатею тестированием, в последнее время - интеграционным. И всегда хочется покрыть различные области приложения - сначала простые функции юнит тестами.. потом контроллеры моками, потом API интеграционными, наконец UI системными.. но почта для меня всегда оставалась недоступным горизонтом - а ведь хочется автоматизировать и то что почта приходит правильная.. и что вообще отсылается.

Под никсы есть два основных почтовых сервера - sendmail и postfix. Я работаю с последним. 

Моки в юнит-тестах

· 2 мин. чтения

Моки это такие классы-заглушки (и соответсвенно объекты), которые позволяют избавиться от внешних зависимостей при модульном (unit) тестировании — ибо тестирование с зависимостями уже интеграционное или системное и требует больших ресурсов, состояния данных и как следствие - большей сложности.

С моками же можно тестировать контроллеры, где код с большей ответсвенностью, который вызывает тяжёлые модели, у которых вся тяжёлая логика веб-сервисов, баз данных, парсеров.. но которые уже покрыты тестами.

В PHPUnit моки встроены в PHPUnit_Framework_TestCase и легко получаются от метода getMock(класс, методы, ...). Сам по себе он не только даёт объект, но и определяет класс. Так что на случай использования статического метода, мок должен сработать.

$myMock = $this->getMock('MyParser', array('parseXLS'));

Проектирование сайтов для людей с ограниченными возможностями

· 5 мин. чтения

Выживает не самый сильный и не самый умный, а тот, кто лучше всех откликается на происходящие изменения Леон Меггинсон, перефразируя Чарльза Дарвина

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

О недоступности веб-шрифтов при подгрузке

· 1 мин. чтения

Лет пять назад я писал про типографию для web через @font-face. С того времени прошло достаточно времени, браузеры уже стали полноценно поддерживать шрифты. Для простоты и единства, вышел конструктор Google Fonts и Typekit. По всей истории этого дела хорошо прошёлся Вадим Макеев, поэтому я сфокусируюсь на основном баге.

Аж с 2009 года (а то и раньше) был известен баг многих браузеров об отображении текста без символов из-за того что css подгрузился, указал что должен использоваться какой-то шрифт, а сам шрифт (который может нехило весить) ещё грузится. В зависимости от скорости соединения возникает эффект "моргания". Проблема в том что это до сих пор не исправлено в Webkit (Safari, Chrome), поэтому вот мой уродливый хак (да, избавьтесь как только пофиксят)..

Прежде всего  через jquery заставим загружаться файл шрифтов, добавив пустой элемент.

$('body').append('<span style="font-family: \'CustomFontYouWantToUse\';"></span>');

Поймать событие когда шрифт загрузится получается только таким способом.

document.onreadystatechange = function() { if (document.readyState && document.readyState=="complete"){ $('body').addClass('font_loaded'); } };

Теперь в css к стандартному шрифту прийдётся добавить дубликат..

h1 {font-family:serif;} body.font_loaded h1{font-family:CustomFontYOuWantToUse, serif;}На просторах интернета было замечено интересное решение, подгружать содержание шрифта сразу в css файле, используя data:URL src:url(data:font/opentype;base64

Тестирование внутренних методов с PHPUnit и runkit

· 2 мин. чтения

PHPUnit хороший инструмент для тестирования PHP-кода. Но у него есть существенные ограничения влияющие его использование рядовыми программистами. Я даже не говорю об установке. Дело в том что юнит-тесты подразумевают

public function methodIWantToTest(){
$s='';
for($i=1;$i<10;$i++){
$s.=$this->innerMethodIWantToAvoidCalling($i);
}
return $s;
}

Kohana

· 1 мин. чтения

Kohana - один из десятка php-фреймворков, имеющий в том числе и модуль для юнит-тестирования написанного кода. Про тестирование в общем я уже писал, про настройку phpunit тоже. Упростив статью одного безымянного товарища, в сторону использования PHPStorm IDE вот к чему я пришёл..

Skyrim

· 9 мин. чтения

Skyrim это уже пятая ролевая игра из эпичной серии TES от Bethesda. Для незнакомых с серией, напомню что дело происходит на планете Нирн c основным континентом Тамриэль с множеством животных, рас, языков; во время, по духу очень напоминающему распад Римской империи и начало раннего средневековья. Но прежде, немного космологии..

Backbone.js

· 4 мин. чтения

Backbone это javascript-библиотека для тяжёлых frontend javascript приложений, как например gmail или twitter. В таких приложениях вся логика интерфейса ложится на браузер, что впрочем даёт очень значительное преимущество в скорости интерфейса. А как организовать код, для таких достаточно больших проектов? Который не просто набор глобальных переменных и jquery—DOM связей. Для меня перенос бэкэнда вперёд был довольно непривычен.

Backbone близок по духу еще одной библиотеке для богатого фронта - knockout, но как можно судить по названиям, они отличаются по замыслу. Нокаут занимается активной Позвоночни же отвечает только за достаточную многослойность архитектуры, которую надо программисту унаследовать.

Основные классы

В качестве ядра используются наследуемые "классы"

  • Router и History - принимают url и говорят какой view надо запустить
  • View - привязывается к dom-элементам и в зависимости от их вложённости отвечает за их данные. Именно View вызываются из Route что-бы открыть прошлое состояние. И именно View реагирует на пользователя через подписку на события ( поле events)
  • Collection - массив моделей (не обязательно одинаковых), привязывается к View и может оповещать его об изменениях
  • Model - основные классы сущностей, имеют url для получения и изменения данных по RESTful http с backend

View, будучи конечным результатом и наиболее нагруженным классом, напрямую зависит от js-шаблонизатора из underscore библиотеки. Шаблонизатор ничем практически не отличается - только тем что все шаблоны подгружаются сразу в script-тэгах и переменные выделяются <%=foobar%> тэгами с мини-логикой. Начало выглядит примерно так..

MyApp.EditableSpanView = Backbone.View.extend({
template: _.template($('#span_wrapper').html()),

initialize: function(){
this.render();
},

render:function(){
$(this.el).html(this.template(this.model.toJSON()));
}
});

Это объявление View с 1-1 зависимостью с DOM элементом #span_wrapper откуда мы возьмём html конструкцию и модель this.model с данными, которая ещё не задана. Документация backbone не блещет деталями, о том присвоить < model или collection если он не общий. Как-то так:

realSpanView = new MyApp.EditableSpanView({model: ItemModel, id:'output' });

Это реализация View. Тут выполнится унаследованные конструктор initialize и рисование в render. Результат будет в элементе с id output — если он уже на странице имеется то в нем, если нет то появится.

События

Backbone имеет своё представление о том как должны распространятся события. Я когда-то писал о сложности этого в DOM. Тут же всё проще. События распространяются двумя путями

  1. От DOM-элемента через View.events в соответствующий написанный вами View-метод

  2. От модели или коллекции через подписанные на неё объекты выше (см. диаграмму выше), в т.ч. View

Насчёт первого пункта можно добавить что имеет смысл делать вложенные View-объекты (как группы DOM-деревьев). Например MyBodyView для глобально позиционируемых слоёв, MyListView с коллекцией и MyItemView для конкретного элемента.

По второму пункту - есть магический метод this.bind, который связывает два типа объектов, обычно в initialize. Например MyListView.initialize имеет смысл связать view с коллекцией, что-бы изменение коллекции вызывало изменение view. 

this.collection.bind('add', this.add, this); 
this.collection.bind('remove', this.remove, this);

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

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

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

$params = (array)json_decode(file_get_contents('php://input'));

Например вам необходимо постоянно поддерживать свежесть коллекции. Коллекция загружается по url параметру из fetch и дальше вызывается reset где обычно коллекция наполняется опять моделями. 

Так вот для того что-бы сделать преобработку данных без перезаписи собственно reset метода, я вешаю bind на reset, вот только аргумент результатов - не json-список и не список переконвертированных моделей у которых распечатать свойства можно только через .get(), а непонятный формат в котором впрочем есть models параметр по которому можной пройтись.

И такого непривычного поведения достаточно что-бы выбить из колеи. То же ключевое слово this - при bind теряется и на самом деле используется связанный объект (коллекция), для этого есть третий параметр или же bindAll из underscore.

Вывод

Библиотека интересная, ограничивающая программистов привыкших к расхлябанному jquery-подходу подвешивания событий на DOM и принуждающая frontend писать едва ли не так же качественно как и backend. Поэтому использовать её стоит в ограниченных условиях, когда надо написать больше JS-кода чем обычно. Альтернативы Backbone — Spine.jsSproutcoreCappuccinoEJS. Из "почитать по теме" советую подкаст sitepoint 145bbtutorialsхабр, а для особо одарённых — про тестирование с Jasmine.

О целостности

· 5 мин. чтения

Целостность, она же Интеграция (отсюда integrity - честность), это такая знакомая политикам программистам, математикам сущность, без которой невозможна мысль и жизнь. Ведь что такое ассоциация как не обобщение, абстрагирование от квадрата и треугольника до Формы. Убери обобщение, которое и ведёт к целостности, и мысль будет бегать на низком уровне рефлексов.

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

Но обобщение происходит не ради самого себя. В основе лежит... 

Мини-шпаргалка по Unix

· 6 мин. чтения

С  юниксом я до некоторых пор был в достаточно прохладных отношениях - мы встречались довольно редко, каждый раз у него были новые дистрибутивы и формы. То в университете Солярис удивлял своим UI, то на хостинге каком-то надо покопаться через ssh. В какой-то момент я стал пристальней посматривать за его возможностями, удивляясь популярности. Наконец я поставил все основные дистрибутивы на виртуальные машины. Ленивый Slackware сопротивлялся больше всех. На этом казалось можно было бы и поставить точку в нашем романе. Но теперь я работаю на маке и приходится разбираться основательней. Основное преимущество *никсов это конечно консоль и гибкая обработка файлов и текста из неё.

Итак, есть много основанных на unix систем - Solaris, MacOSX, FreeBSD и в частности Linux, у которого основные дистрибутивы: Debian - Ubuntu, Fedora - Red Hat, Slackware - Suse, Gentoo, Mandriva

Первое с чем столкнётся пользователь терминала это его интерфейс, hotkeys для быстрой работы

TabАвтодополнение пути (для папок и файлов) при навигации
Предыдущая комманда
CtrlCУбить исполняемую программу (Cancel)
CtrlZПереключить вид из программы. Комманды fg, jobs, bg помогают управлять контекстом
QВыход из некоторых программ (как например man - помощи)

Навигация

Теперь основная часть - ходить по файловой системе, будь то локальной или удалённой.

lsсписок файлов и папок
cdперейти в папку
pwdузнать в какой папке я нахожусь
tailбыстрый просмотр конца файла (полезно для логов)
catбыстрый просмотр содержимого файла
tree -d -L 2двух уровневое дерево папок
find . -name *txtпоиск файла по имени
grep "needle" file.txtпоиск по паттерну
whoamiузнать какой я пользователь
uname -aузнать что за ядро (и может дистрибутив)

Файловая структура

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

FSSTND СтандартSuse 11Mac OS 10.7Описание
bootmach_kernelФайлы загрузчика. Lilo и образ ядра
tmpprivate/tmpВременные фейлы для бута. Остальным - в /var/tmp
sbinСистемные комманды нужные при буте
binПользовательские комманды нужные при буте
procВиртуальная папка настроек и процессов ядра
devПодключённые устройства
mntVolumesДоступные через файловую систему устройства
libОбщедоступные библиотеки root-уровня
etcprivate/etcКонфигурационные файлы, зависящие от машины
rootprivate/var/rootДомашняя папка главного пользователя
homeДомашние папки остальных пользователей, в том числе ftp, httpd, samba-сервисов
usrCтатичные файлы программ, библиотек, игр, документации, утилит не используемых при загрузке
varprivate/varЧасто меняющиеся файлы - почтовые сообщения, логи, временные файлы
cdrom
opt+
image
lost+found
media
selinux
srvПапка для апача, корень документов вместо htdocs
netмаунт для NFS дисков
privateсюда сгруппированы разные папки для ограничения доступа
coresместо для сохранения дампов памяти при падении.. можно почистить при недостатке места
ApplicationsВсе установленные пользователем приложения
Developer
LibraryОбщедоступные файлы (например шрифты) межпрограммного уровя
Network
System
Usersдомашние папки настоящих пользователей, расширение home

Комбинирование

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

|труба - передача данных от одной комманды в другую, например ls -F | grep /
;последовательный запуск комманд, например date;cal
>перенаправление стандартного вывода в файл с перезаписью, например ls / -F > list.txt
>>перенаправление вывода в файл с добавлением в конец

Обработка файлов

mvпереименовывание
rmудаление
rm -rfрекурсивное удаление
catобъединить файлы и выдать в поток
pasteобъекдинить строки файла(-ов)
lnссылка на файл или папку
splitбинарное разделение файла
joinпострочно объединяет два файла по общему слову
cutпострочно (и/или) поколоночное вырезание текста из файла как из матрицы символов
catпоследовательное объединение двух файлов и вывод в терминал (или файл)
sortотсортировать строчки по алфавиту
uniqоставить уникальные строчки

Установка программ

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

ДистрибутивУстановщик пакетов
Gentooemerge
Debianapt-get update
apt-get install ...
dpkg --install
Fedorarpm -ivh ...
yum -y install ...Гляньте также на возможные репозитории
Mandrivaurpm
Slackwareyast, zypper
gzip, lzma

Кроме системных общих установщиков, есть и специфичные под конкретные нужды установщики

  • port install mongodb -  Установщик macports 

  • pear install phpunit/PHPUnit

  • pecl install mongo

  • gem

Текстовые редакторы

ViVimNanoGeditMatePicoEmacs
Ubuntu+++-
Mac OS Lion++-+++
Debian 5+++-+-
Suse 11+-----

vi readme.txt
i - режим редактирования
esc - выход в общий режим
ZZ - сохранить и выйти
" - режим консоли
!q - выход без сохранения
wq - выход с записью

Архиваторы

gunzipраспаковать .gz
bunzip2распаковать .bz2
tar -jxvfраспаковать .tar

Процессы и диагностика

topзапущенные процессы. См. также htop, pstree
netstatоткрытые соединения
например открытые порты в линуксе: netstat -atp | grep -i "listen" или netstat -tupl
на маке: lsof -n -i4TCP | grep LISTEN
lsofоткрытые файлы (например открытые порты в маке: sudo lsof -i -P|grep -i "listen")
killallубить процесс по имени. Иногда приходится убивать точечно (например подвисший mysql) по PID:
kill -9 1566
whereisподсказка где находятся файлы процесса
df -hотчёт о свободной HDD-памяти (в читаемой форме). Что-бы найти большие файлы на сервере:
find . -type f -size +50000k -exec ls -lh {} ; \| awk '{ print $9 ": " $5 }'

| |w|список пользователей в данный момент| |opensnoop|слежка за доступом к файловым указателям|

Работа с серверами

Практически все постоянно работающие программы - демоны находятся в /etc/init.d/ и имеют комманды start, stop, restart. Необходимы права рута для их управления. В некоторых случаях (Fedora) может прийдётся использовать /sbin/service для доступа к ним

sshУдалённое соединение
scpБезопасное сетевое копирование через SSH с одного сервера на другой. Флаг -r делает рекурсивное копирование папок. Например:
scp -r root@example.com:/srv/htdocs/ /home/html_sources/
ab(Apache Bench) - нагрузочное тестирование (например с параметрами -r -c 100 -n 1000)
nc(netcat) - прямое сокет-соединение
rsyncСинхронизация файлов rsync -av -e ssh --progress /local/source user@example.com:remote/target
Apache

На маке апач2 встроен (включается в настройках sharing) и в нём имеет смысл включить .htaccess (см. директиву AllowOverride)

LinuxMac OSX
Запуск/etc/init.d/httpd restart/etc/init.d/apache2 restart/etc/apache2/
/usr/sbin/apachectl restart
/private/etc/apache2/httpd.conf/private/var/log/httpd/access_log
Логи/private/var/log/apache2/access_log
/private/var/log/httpd/access_log
PHP
Suse 11Mac OSX
/etc/php5/apache2/php.ini
/usr/share/php5/
/usr/lib64/php5//private/etc/php.ini
/usr/local/php5/lib/php/extensions/
MySQL

Настройки MySQL по умолчанию используются зашитые (их можно спросить у mysqladmin посмотреть), для перезаписи на свои, надо в /etc положить my.cnf файлик (образец есть в папке support-files). Самые полезные утилиты в bin папке это mysql (прямой консольный доступ к серверу) и mysqldump (для миграции больших баз).

Если вы сидите на маке, но хочется через виртуалку использовать sqlyog, можно расшарить соединение..

CREATE USER 'your_user_id'@'localhost' IDENTIFIED BY 'your_password'; GRANT ALL ON your_database_name.* TO your_user_id@'%' IDENTIFIED BY 'your_password';

При быстрой работе с самим сервером наиболее востребованы комманды

SHOW PROCESSLIST;
SHOW DATABASES;
CHECK TABLE;
REPAIR TABLE;

Управление демоном

LinuxMac OSX
/etc/init.d/mysql restart/usr/local/mysql/bin/mysqld_safe
/usr/local/mysql/bin/mysqladmin shutdown

См. также

Сборка php-проекта с selenium и jenkins

· 5 мин. чтения

В этой статье я теперь попытаюсь описать как собирать php-проект вместе и как проводить deployment так, что-бы разработчики видели прогресс и состояние здоровья проекта с помощью инструментов статического анализа кода, что-бы были видны результаты запуска unit- и selenium- тестов с результатом покрытия, что-бы проверялся принятый стиль кода. В качестве груши для битья я возьму Drupal 7.8 для анализа.