Светильник, который следит за тобой: китайцы встроили камеру в лампочку

Китайская компания-производитель лампочек со встроенными колонками (что уже достаточно необычно) Sengled готовится начать продажи лампочки-видеокамеры Snap («снимок»).

image
Два в одном: лампочка и широкоугольная камера

Лампочка была показана на выставке CES 2015, где сразу была отмечена наградой «лучший инновационный продукт». Встроенная камера снимает в HD разрешении и обладает технологией WDR (WIDE Dynamic Range) для широкоугольной съёмки.

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

image

Генеральный менеджер Sengled в США Алекс Руан сравнивает их лампочки со смартфонами Apple в интервью каналу CNBC. Он говорит, что их компания пытается изменить подход к применению бытовых лампочек, как когда-то Стив Джобс изменил подход к смартфонам:

«Суть умных домов – поменять стиль нашей жизни, улучшить ее качество, и поэтому доступность для каждого – в приоритете у Sengled», – говорит Руан. Тем не менее, цены на их лампочки тоже больше похоже на эппловские: $170 за пару ламп с динамиками.

Сейчас Snap находится на стадии подготовки к производству, и в продажу пока не поступил.

Sengled является одной из самых передовых компаний в сфере LED-технологий. По прогнозам McKinsey, рынок на котором она работает, вырастет до $108 миллиардов к 2020 году. На данный момент ассортимент компании включает в себя три разновидности «умных» лампочек: Puls, Puls Solo и Boost.

Первые две оснащены световыми диодами и встроенными Bluetooth-колонками. Достаточно вкрутить их в лампы, скачать приложение на ваш смартфон, чтобы слушать музыку мелодиями и изменять при этом уровень освещения в один тап.

Третья же является в свою очередь усилителем (репитером) Wi-Fi сигнала и может работать с абсолютно любой системой умного дома. Её тоже достаточно просто вкрутить в лампу и подключить к сети.

image
Puls, Puls Solo и Boost сответственно

Источник


Ещё мы бы хотели, чтобы вы почитали в нашем блоге:

История iRidium mobile: от торговли компьютерами к мировому производителю софта для умных домов

Будущее интернета вещей: разговор с институтом Пратт

ссылка на оригинал статьи http://geektimes.ru/post/247862/

За продолжение работы в России системе Visa придется заплатить $60 млн

Платежной системе Visa придется уплатить $60 млн за право продолжить работу в России. Дело в том, что Visa не успевает перенести все внутрироссийские транзакции на обработку в Национальную систему платежных карт (НСПК), пишет Meduza. Поэтому системе необходимо выплатить гарантийный взнос за квартал, российский регулятор не будет делать поблажек.

При этом существует вероятность того, что депозит Visa будет взыскан в качестве штрафа за приостановку транзакций по картам тех банков, что находятся под санкциями США. В РФ сейчас работает закон, согласно которому за каждый день приостановки работы взимается штраф в размере 10% гарантийного взноса.

По закону гарантийный взнос, составляющий средний оборот платежной системы за два дня, должен платиться не сразу, а поквартально в течение двух лет. За первый квартал 2015 взнос должен быть уплачен до конца апреля. По расчетам специалистов, гарантийный взнос Visa за первый квартал должен составить около 60 миллионов долларов.

ссылка на оригинал статьи http://megamozg.ru/post/13758/

IP-Geo. Оптимизация SQL запроса



Здравствуй, Хабрахабр! Меня зовут Бабичев Максим и я быдлокодер. Это моя первая статья на Хабрахабр, прошу строго не судить.

Нашел на Хабре статью, в которой были базы в двух модификациях: Страны и города и только страны. Также в этих архивах есть небольшие примеры использования таблиц на php.

Сразу хочу сказать, что эта статья рассчитана на новичков, а не на продвинутых гуру.

На всякий случай приложу структуру таблиц
    --     -- Структура таблицы `net_city`     --      CREATE TABLE IF NOT EXISTS `net_city` (         `id`          INT(11) NOT NULL AUTO_INCREMENT,         `country_id`  INT(11)          DEFAULT NULL,         `name_ru`     VARCHAR(100)     DEFAULT NULL,         `name_en`     VARCHAR(100)     DEFAULT NULL,         `region`      VARCHAR(2)       DEFAULT NULL,         `postal_code` VARCHAR(10)      DEFAULT NULL,         `latitude`    VARCHAR(10)      DEFAULT NULL,         `longitude`   VARCHAR(10)      DEFAULT NULL,         PRIMARY KEY (`id`),         KEY `country_id` (`country_id`),         KEY `name_ru` (`name_ru`),         KEY `name_en` (`name_en`)     )     ENGINE = MyISAM     DEFAULT CHARSET = utf8;       --     -- Структура таблицы `net_city_ip`     --      CREATE TABLE IF NOT EXISTS `net_city_ip` (         `city_id`  INT(11)    DEFAULT NULL,         `begin_ip` BIGINT(11) DEFAULT NULL,         `end_ip`   BIGINT(11) DEFAULT NULL,         KEY `city_id` (`city_id`),         KEY `ip` (`begin_ip`)     )     ENGINE = MyISAM     DEFAULT CHARSET = utf8;       --     -- Структура таблицы `net_country`     --      CREATE TABLE IF NOT EXISTS `net_country` (         `id`      INT(11) NOT NULL AUTO_INCREMENT,         `name_ru` VARCHAR(100)     DEFAULT NULL,         `name_en` VARCHAR(100)     DEFAULT NULL,         `code`    VARCHAR(2)       DEFAULT NULL,         PRIMARY KEY (`id`),         KEY `code` (`code`),         KEY `name_en` (`name_en`),         KEY `name_ru` (`name_ru`)     )     ENGINE = MyISAM     DEFAULT CHARSET = utf8;       --     -- Структура таблицы `net_country_ip`     --      CREATE TABLE IF NOT EXISTS `net_country_ip` (         `country_id` INT(11)    DEFAULT '0',         `begin_ip`   BIGINT(11) DEFAULT NULL,         `end_ip`     BIGINT(11) DEFAULT '0',         KEY `country_id` (`country_id`),         KEY `ip` (`begin_ip`)     )     ENGINE = MyISAM     DEFAULT CHARSET = utf8;       --     -- Структура таблицы `net_euro`     --      CREATE TABLE IF NOT EXISTS `net_euro` (         `country_id` INT(11)    DEFAULT '0',         `begin_ip`   BIGINT(11) DEFAULT NULL,         `end_ip`     BIGINT(11) DEFAULT '0',         KEY `country_id` (`country_id`),         KEY `ip` (`begin_ip`)     )     ENGINE = MyISAM     DEFAULT CHARSET = utf8;       --     -- Структура таблицы `net_ru`     --      CREATE TABLE IF NOT EXISTS `net_ru` (         `city_id`  INT(11)    DEFAULT '0',         `begin_ip` BIGINT(11) DEFAULT NULL,         `end_ip`   BIGINT(11) DEFAULT NULL,         KEY `city_id` (`city_id`),         KEY `ip` (`begin_ip`)     )     ENGINE = MyISAM     DEFAULT CHARSET = utf8;     



Меня больше интересуют запросы SQL.

LONG_IP_ADDRESS, число полученное с помощью функции ip2long() в PHP.

-- Ищем по российским и украинским городам -- Запрос (1)  SELECT * FROM (     SELECT *     FROM net_ru     WHERE begin_ip <= LONG_IP_ADDRESS – IP пользователя, ip2long()     ORDER BY begin_ip DESC     LIMIT 1 ) AS t WHERE end_ip >= LONG_IP_ADDRESS – IP пользователя long 


После запроса (1), получают нужный город из таблицы net_city:

-- Запрос (2)  SELECT * FROM net_city WHERE id = -- (Результат из первого запроса).city_id 


Код из примера на PHP:

<?php // Подключаемся к базе данных $db_host = "localhost"; $db_user = ""; $db_password = ""; $db_database = "geo"; $link = mysql_connect ($db_host, $db_user, $db_password); if ($link && mysql_select_db ($db_database)) {     mysql_query ("set names utf8"); } else {     die ("db error"); }  // IP-адрес, который нужно проверить $ip = "79.134.219.2";  // Преобразуем IP в число $int = sprintf("%u", ip2long($ip));  $country_name = ""; $country_id = 0;  $city_name = ""; $city_id = 0;  // Ищем по российским и украинским городам $sql = "select * from (select * from net_ru where begin_ip<=$int order by begin_ip desc limit 1) as t where end_ip>=$int"; $result = mysql_query($sql); if ($row = mysql_fetch_array($result)) {     $city_id = $row['city_id'];     $sql = "select * from net_city where id='$city_id'";     $result = mysql_query($sql);     if ($row = mysql_fetch_array($result)) {         $city_name = $row['name_ru'];         $country_id = $row['country_id'];     } else {         $city_id = 0;     } } 


Избавимся от подзапроса в запросе (1), используя AND.

-- Модифицированный запрос (3)  SELECT `city_id` FROM `net_ru` WHERE begin_ip <= LONG_IP_ADDRESS AND end_ip >= LONG_IP_ADDRESS 


И в этом случае мы избавимся от подзапроса. Но вспомним про BETWEEN и запишем запрос так:

-- Модифицированный запрос (4)  SELECT `city_id` FROM `net_ru` WHERE LONG_IP_ADDRESS BETWEEN begin_ip AND end_ip 



Так SQL-код читабельнее и короче.

Остался отдельный запрос на город. Объединим модифицированный запрос (4) и (2).

-- Модифицированный запрос (5)  SELECT * FROM `net_city` `city`     JOIN (         SELECT `city_id`         FROM `net_ru`         WHERE LONG_IP_ADDRESS BETWEEN begin_ip AND end_ip     ) AS `res` ON `res`.`city_id` = `city`.`id` 


Так получилось, что IP-адреса 79.134.219.2 в базе net_ru – нет. Но он есть в базе net_city_ip.
На многих ресурсах объединяют запросы так:

-- Модифицированный запос (5.1)  SELECT * FROM `net_city` `city`     JOIN (         SELECT `city_id`         FROM `net_ru`         WHERE LONG_IP_ADDRESS BETWEEN begin_ip AND end_ip     ) AS `res` ON `res`.`city_id` = `city`.`id` UNION SELECT * FROM `net_city` `city`     JOIN (         SELECT `city_id`         FROM `net_city_ip`         WHERE LONG_IP_ADDRESS BETWEEN begin_ip AND end_ip     ) AS `res` ON `res`.`city_id` = `city`.`id` 


Видим, что запросы идентичны. Объединим внутри JOIN два запроса, получим:

-- Модифицированный запрос (5.2)  SELECT * FROM `net_city` `city`     JOIN (         SELECT `city_id`         FROM `net_ru`         WHERE LONG_IP_ADDRESS BETWEEN  begin_ip  AND end_ip         UNION         SELECT `city_id`         FROM `net_city_ip`         WHERE LONG_IP_ADDRESS BETWEEN  begin_ip  AND end_ip     ) as `res` ON `res`.`city_id` = `city`.`id` 


Модифицированный запрос (5.2) хорош собой, но нам не нужны все поля.
Вытащим:

  1. name_ru
  2. name_en
  3. region
  4. postal_code
  5. latitude
  6. longitude



-- Модифицированный запрос (6)  SELECT DISTINCT     `city`.`name_ru` `city_name_ru`,     `city`.`name_en` `city_name_en`,     `city`.`region`,     `city`.`postal_code`,     `city`.`latitude`,     `city`.`longitude` FROM `net_city` `city`     JOIN (         SELECT `city_id`         FROM `net_ru`         WHERE LONG_IP_ADDRESS BETWEEN begin_ip AND end_ip         UNION         SELECT `city_id`         FROM `net_city_ip`         WHERE LONG_IP_ADDRESS BETWEEN begin_ip AND end_ip     ) AS `res` ON `res`.`city_id` = `city`.`id` 


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

-- Модифицированный запрос (7)  SELECT DISTINCT     `city`.`name_ru`    `city_name_ru`,     `city`.`name_en`    `city_name_en`,     `city`.`region`,     `city`.`postal_code`,     `city`.`latitude`,     `city`.`longitude`,     `country`.`name_ru` `country_name_ru`,     `country`.`name_en` `country_name_en`,     `country`.`code` FROM `net_city` `city`     JOIN (         SELECT `city_id`         FROM `net_ru`         WHERE LONG_IP_ADDRESS BETWEEN begin_ip AND end_ip         UNION         SELECT `city_id`         FROM `net_city_ip`         WHERE LONG_IP_ADDRESS BETWEEN begin_ip AND end_ip     ) AS `res` ON `res`.`city_id` = `city`.`id`     JOIN `net_country` `country`         ON `country`.`id` = `city`.`country_id` 


Протестируем запрос.

Для этого получим с помощью PHP значение LANG_IP_ADDRESS:

<?php echo ip2lang(‘79.134.219.2’); # Результат: 1334237954 


Подставим его в наш запрос и выполним в phpMyAdmin.

Модифицированный запрос (7) с подставленным значением
-- Модифицированный запрос (7) с подставленным значением  -- ip2lang(‘79.134.219.2’) вместо LONG_IP_ADDRESS  SELECT DISTINCT     `city`.`name_ru`    `city_name_ru`,     `city`.`name_en`    `city_name_en`,     `city`.`region`,     `city`.`postal_code`,     `city`.`latitude`,     `city`.`longitude`,     `country`.`name_ru` `country_name_ru`,     `country`.`name_en` `country_name_en`,     `country`.`code` FROM `net_city` `city`     JOIN (         SELECT `city_id`         FROM `net_ru`         WHERE 1334237954 BETWEEN begin_ip AND end_ip         UNION         SELECT `city_id`         FROM `net_city_ip`         WHERE 1334237954 BETWEEN begin_ip AND end_ip     ) AS `res` ON `res`.`city_id` = `city`.`id`     JOIN `net_country` `country`         ON `country`.`id` = `city`.`country_id` 



Результат работы запроса:



1 всего, запрос занял 0.3408 сек.

JOIN соединения сильно влияют на скорость выполнения запроса. Запишем JOIN с помощью WHERE

-- Модифицированный запрос (8)  -- Записали JOIN с помощью WHERE  SELECT DISTINCT     `city`.`name_ru` `city_name_ru`,     `city`.`name_en` `city_name_en`,     `city`.`region`,     `city`.`postal_code`,     `city`.`latitude`,     `city`.`longitude`,     `country`.`name_ru` `country_name_ru`,     `country`.`name_en` `country_name_en`,     `country`.`code` FROM `net_city` `city`     JOIN `net_country` `country`         ON `country`.`id` = `city`.`country_id` WHERE `city`.`id` = (     SELECT `city_id`     FROM `net_city_ip`     WHERE LONG_IP_ADDRESS BETWEEN `begin_ip` AND `end_ip`     UNION     SELECT `city_id`     FROM `net_ru`     WHERE LONG_IP_ADDRESS BETWEEN `begin_ip` AND `end_ip` ) 


Протестируем модифицированный запрос:

Модифицированный запрос с помощью WHERE
SELECT DISTINCT     `city`.`name_ru` `city_name_ru`,     `city`.`name_en` `city_name_en`,     `city`.`region`,     `city`.`postal_code`,     `city`.`latitude`,     `city`.`longitude`,     `country`.`name_ru` `country_name_ru`,     `country`.`name_en` `country_name_en`,     `country`.`code` FROM `net_city` `city`     JOIN `net_country` `country`         ON `country`.`id` = `city`.`country_id` WHERE `city`.`id` = (     SELECT `city_id`     FROM `net_city_ip`     WHERE 1334237954 BETWEEN `begin_ip` AND `end_ip`     UNION     SELECT `city_id`     FROM `net_ru`     WHERE 1334237954 BETWEEN `begin_ip` AND `end_ip` ) 



Результат работы запроса:



1 всего, запрос занял 0.1527 сек.

Модифицированный запрос был выполнен быстрее более чем в два раза.

Аналогичный запрос можно получить и для стран.

Данная Geo-IP база была выбрана для объяснения JOIN/UNION соединений и оптимизации запросов.
Надеюсь, данная статья поможет начинающим понять, насколько важна оптимизация и как её можно добиться. Рекомендую к чтению статью пользователя tuta_larson.

Данная Geo-IP база очень старая и знает очень мало IP-адресов. Но вы можете составить свою IP-базу и с помощью пользователей пополнять её.



Дальше составить рейтинг IP и основываясь на собственный рейтинг “угадывать” город из которого пользователь.

База данных и информацию по GeoIP брал из статьи: «База GeoIP – страны и города, сентябрь 2013». Спасибо пользователю netload за увлекательную статью, написаную в 2013 году.

ссылка на оригинал статьи http://habrahabr.ru/post/254461/

Приглашения на работу прячут в заголовки HTTP

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

Это довольно банальный приём, но в последнее время компании придумали новые трюки. Специалист по безопасности Трой Хант рассказал о случае, который произошёл во время занятий по информационной безопасности в Сиднее. Один из студентов во время выполнения упражнения из курса Сначала хакни свои API (платный курс Ханта) обратил внимание на необычный HTTP-ответ, который пришёл через мобильные API с сервера Airbnb:

X-Hi-Human: The Production Infrastructure team added this header. Come work with us! Email kevin.rice+hiring@airbnb.com

Классно, заголовок HTTP, сделанный специально для людей! Конечно, его увидят только те, кто действительно изучает этот трафик. Ну и те, кто читает блог Троя Ханта и Geektimes. Там указан адрес электронной почты Кевина Райса, и это не какой-то тип из отдела кадров, а руководитель отдела разработки (Engineering Manager). Правда, сейчас ему придётся родить новую пасхалку, потому что старая спалилась.

Это не единственная выдумка для привлечения разработчиков. Например, даже Microsoft вставила HR-объявление в код страницы Microsoft Azure. Ссылка отправляет на страницу Microsoft Careers.



Гораздо больше креатива проявил Flickr со своим огромным ASCII-артом на главной странице.

А вы когда-нибудь встречали в коде предложение о работе?


Никто ещё не голосовал. Воздержавшихся нет.

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

ссылка на оригинал статьи http://geektimes.ru/post/248142/

Как переписать большой проект или безболезненный для бизнеса рефакторинг



Вопрос, который мне задают чаще всего, — как разговаривать о рефакторинге с руководителем?
В таких случаях я даю несколько спорный совет: не говорите ему ничего!

Мартин Фаулер, «Рефакторинг. Улучшение существующего кода»

Устаревание кода, трудности с поддержкой, непредсказуемые баги — эти термины один за другим появляются в жизни разработчика по мере разработки продукта. И если первое — это скорее интересы разработчика, то последнее — это прямая проблема бизнеса.

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


Разбор полетов



Проблемы


Они обычно начинаются по известному сценарию:

  1. Прибегает начальник с воплями «У нас ничего не работает, главный клиент под угрозой!»;
  2. или менеджер с просьбой прикрутить нереализуемую фишку;
  3. реже мы, разработчики, настолько устаем копаться в говне «легаси»-коде, что решаем переписать всё.


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

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


Задачи


  1. Перевести проект на современную архитектуру
  2. Обеспечить минимальные затраты на рефакторинг



Принципиальная схема реализации


Наш проект был изначально написан на Kohana, переписывали мы его на Symfony2, поэтому все примеры приведены в контексте этих систем. Однако, данный подход можно применять с любыми фреймворками. Необходимое требование — единая точка входа в приложение.

Изначально приложение обрабатывает запросы пользователя через точку входа «app_kohana.php»

Мы будем оборачивать начальную точку входа в новой системе, организовывая своеобразный «прокси».


Рефакторинг



Контроллер — обертка для старой системы


Идея довольно проста и заключается в следующем:

  1. Разворачиваем параллельно две системы (kohana + symfony)
  2. Меняем точку входа на новую (symfony)
  3. Организуем универсальный контроллер, который по умолчанию будет пробрасывать все запросы в старую систему



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

Первое, что приходит в голову — обернуть инклюд в ob_start. Так и сделаем:

class PassThroughController extends Symfony\Bundle\FrameworkBundle\Controller\Controller {     public function kohanaAction()     {         ob_start();         $kohanaPath = $this->container->getParameter('kernel.root_dir') . '/../app_kohana.php';         include $kohanaPath;         $response = ob_get_clean();         return new Response($response);     } } 


Роутинг для универсального контролера
application.passthrough_kohana:     path: /{slug}     defaults:         _controller: ApplicationBundle:PassThrough:kohana     requirements:         slug: .* 




В таком формате система уже работает, но спустя какое-то время прилетает первый баг. Например, некорректная обработка ajax-ошибок. Или на сайте ошибки отдаются с кодом 200 вместо 404.

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

class PassThroughController extends Symfony\Bundle\FrameworkBundle\Controller\Controller {     public function kohanaAction()     {         ob_start();         $kohanaPath = $this->container->getParameter('kernel.root_dir') . '/../app_kohana.php';         include $kohanaPath;          $headers = headers_list();         $code = http_response_code();         $response = ob_get_clean();          return new Response($response, $code, $headers);     } } 


После этого полёт нормальный.


Проблемы старой системы, влияющие на функционирование новой



exit()


У нас в системе нашлись места, где в конце работы контроллера радостно вызывался exit(). Это практикуется, например, в Yii (CApplication::end()). Особой головной боли это не доставляет до тех пор, пока не начинаешь использовать событийную модель в новой системе и обрабатывать события, случающиеся после выполнения контроллера. Самый яркий пример — Symfony Profiler, который прекращает работать для запросов с exit’ом.
Данный случай нужно иметь в виду и при необходимости предпринимать соответствующие меры.


ob_end_*()


Необдуманное использование функций ob_end легко может поломать работу новой системы, очистив буфер нового прокси-контроллера. Следует так же иметь в виду.


Kohana_Controller_Template::$auto_render


Переменная отвечает за автоматическую отрисовку полученных из контроллера данных в глобальном шаблоне (может сильно зависеть от используемого шаблонизатора). Во время адаптации новой системы это может сэкономить время на отладку в местах, где, например, json выводится простым echo $json; exit();. Контроллер примет примерно следующий вид:

$this->auto_render = false; echo $json; return; 




О чем еще стоит позаботиться


Описанные выше точки входа — это идеальная ситуация. У нас изначально точка входа была app.php и требовалось, чтобы после рефакторинга она же и осталась (переконфигурирование многочисленных серверов выглядело бесперспективным). Выбран был следующий алгоритм:

  1. Переименовываем app.php в app_kohana.php
  2. Точку входа симфони размещаем в app.php
  3. Profit


И все, казалось бы, завелось, кроме консольных команд, которые в кохане запускались через тот же файл. Поэтому в начале нового app.php родился следующий костылик для обратной совместимости:

if (PHP_SAPI == 'cli') {     include 'app_kohana.php';     return; } 




Жизнь после рефакторинга



Новые контроллеры


Все новые контроллеры мы стараемся писать в symfony. Разделение происходит на уровне роутинга, перед «универсальным» маршрутом дописывается нужный, и Kohana дальше не загружается. Пока мы пишем в новой системе только ajax-контроллеры, поэтому вопрос с переиспользованием шаблонов (Twig) остается открытым.


БД и Конфигурация


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

Application/Resources/config/kohana.php
/** @var \Symfony\Component\DependencyInjection\ContainerBuilder $container */ $kohanaDatabaseConfig = []; $kohanaConfigPath = $container->getParameter('kernel.root_dir') . '/config';  if (!defined('SYSPATH')) {     define('SYSPATH', realpath($container->getParameter('kernel.root_dir') . '/../vendor/kohana/core') . '/'); }  $mainConfig = $kohanaConfigPath . '/database.php'; if (file_exists($mainConfig)) {     $kohanaDatabaseConfig = include $mainConfig; }  if (isset($_SERVER['PLATFORM'])) {     $kohanaEnvConfig = $kohanaConfigPath . '/' . $_SERVER['PLATFORM'] . '/database.php';     if (file_exists($kohanaEnvConfig)) {         $kohanaDatabaseConfig = array_merge($kohanaDatabaseConfig, include $kohanaEnvConfig);     } }  if (empty($kohanaDatabaseConfig['default'])) {     throw new \Symfony\Component\Filesystem\Exception\FileNotFoundException('Could not load database config'); }  $dbParams = $kohanaDatabaseConfig['default'];  $container->getParameterBag()->add([     'database_driver'   => 'pdo_mysql',     'database_host'     => $dbParams['connection']['hostname'],     'database_port'     => null,     'database_name'     => $dbParams['connection']['database'],     'database_user'     => $dbParams['connection']['username'],     'database_password' => $dbParams['connection']['password'], ]); 


Подключается конфиг стандартным способом

Application/DependencyInjection/ApplicationExtension.php
class ApplicationExtension extends Symfony\Component\HttpKernel\DependencyInjection\Extension {     public function load(array $configs, ContainerBuilder $container) {         $loader = new Loader\PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));         $loader->load('kohana.php');     } } 



Как продолжать: вынесение функционала в сервисы


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

Переопределяем системный класс коханы, добавляем туда свойство для контейнера.
class Kohana extends Kohana_Core {     /**     * @var Symfony\Component\DependencyInjection\ContainerBuilder     */     public static $di; } 


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

/** app_kohana_init.php */ // тут инициализация фреймворка, включая системные константы и bootstrap  /** app_kohana_run.php */ echo Request::factory(TRUE, array(), FALSE)     ->execute()     ->send_headers(TRUE)     ->body();  /** app_kohana.php */ include 'app_kohana_init.php'; include 'app_kohana_run.php'; 



Модифицируем наш контроллер, проделывая похожие с app_kohana.php операции, но добавляя между инклюдами проброс контейнера

public function kohanaAction() {     ob_start();      $kohanaPath = $this->container->getParameter('kernel.root_dir') . '/..';     include $kohanaPath . '/app_kohana_init.php';     \Kohana::$di = $this->container;     include $kohanaPath . '/app_kohana_run.php';      $headers = headers_list();     $code = http_response_code();     $response = ob_get_clean();      return new Response($response, $code, $headers); } 



После этого мы в старой системе можем использовать DI-контейнер и все объявленные в новой системе сервисы, включая EntityManager и новые модели доктрины.


Напоследок



Плюсы реализации


  • Мы сделали первый шаг для дальнейшего развития системы.
  • Новая система независима от старой. Весь новый код работает без участия старого
  • Минимум потраченного времени




Минусы реализации


  • Дополнительные накладные ресурсы на «обертку» во время работы со старой частью системы. Однако, по сравнению с задержками в старой системе, оверхедом (как по памяти, так и по процессору) можно пренебречь.
  • Новая система независима от старой. Мы не можем использовать старый код в новой, но это скорее плюс, раз уж мы решились переписывать.
  • Приходится поддерживать модели в двух местах.



Спасибо, что дочитали до конца, желаю успехов в рефакторинге, смахните накопившуюся пыль со старого кода!
И простите за ужасные шрифты на диаграммах :(

ссылка на оригинал статьи http://habrahabr.ru/post/252405/