Перевернуть строку на PHP

Самая популярная задача на собеседованиях это переворот строки без использования функции srtrev. Решил добавить свои 5 копеек, какой я вижу эту задачу.

Первое решение на знание других встроенных функций (никто ведь не запрещал использовать их).

$s = "123abc";

preg_match_all('/./u', $s, $a);
echo implode('', array_reverse($a[0]));

Второе решение с использованием операции конкатенации выглядит еще проще.

$s = "123abc";

for ($i = strlen($s); $i >= 0; $i--) {
    $s .= $s[$i];
    $s[$i] = '';
}

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

$s = "123abc";
$a = -1;
$b = strlen($b);

while (++$a < --$b) {
    $s[$a] = $s[$a] ^ $s[$b];
    $s[$b] = $s[$a] ^ $s[$b];
    $s[$a] = $s[$a] ^ $s[$b];
}

Сниппет для форматирования телефонных номеров

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

В php есть такие функции как money_format и number_format, но нет такой функции как phone_format, этот пробел я и решил восполнить написав такую функцию для форматирования телефонных номеров.

/**
 * Форматирование телефонного номера
 * по шаблону и маске для замены
 *
 * @param string $phone
 * @param string|array $format
 * @param string $mask
 * @return bool|string
 */
function phone_format($phone, $format, $mask = '#')
{
    $phone = preg_replace('/[^0-9]/', '', $phone);

    if (is_array($format)) {
        if (array_key_exists(strlen($phone), $format)) {
            $format = $format[strlen($phone)];
        } else {
            return false;
        }
    }

    $pattern = '/' . str_repeat('([0-9])?', substr_count($format, $mask)) . '(.*)/';

    $format = preg_replace_callback(
        str_replace('#', $mask, '/([#])/'),
        function () use (&$counter) {
            return '${' . (++$counter) . '}';
        },
        $format
    );

    return ($phone) ? trim(preg_replace($pattern, $format, $phone, 1)) : false;
}

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

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

$phones = array(
    '926 111-2233',
    '9261112233',
    '8 (926) 111 22 33',
    '8 926 111-22-33',
    '559-8833',
    '5598833',
    '',
    'qweqwe'
);

$formats = array(
    '7' => '###-##-##',
    '10' => '+7 (###) ### ####',
    '11' => '# (###) ### ####'
);

foreach ($phones AS $phone) {
    echo phone_format($phone, $formats, '#');
}

Результат выполнения радует глаз:

+7 (926) 111 2233
+7 (926) 111 2233
8 (926) 111 2233
8 (926) 111 2233
559-88-33
559-88-33

Данный сниппет не ставит перед собой цель определить город, регион или какой-либо другой параметр. Кроме того возникнут проблемы при использовании шаблонов для номеров “8 (123) 111-22-33” и “+7 (123) 111-22-33”. Возможно потом придумаю как поступать с такими номерами.

Развертывание Node.js приложения на DotCloud

Dotcloud - это облачная платформа для развертывания приложений. Стек на DotCloud включает в себя более 10 различных сервисов среди которых есть и Node.js.

Мои первые впечатления от DotCloud были весьма положительные. Можно практически забыть о том как развертывать приложение и полностью сосредоточится на коде. Работа по развертыванию сводится к установке DotCloud CLI и настройке файла окружения dotcloud.yml. Установка клиента под Linux/MacOS тривиальна:

sudo easy_install pip
sudo pip install dotcloud

Подготовка к развертыванию

Для развертывания приложения на Dotcloud необходим файл dotcloud.yml, который описывает сервисы к которым приложение будет иметь доступ. Полный список приложений можно посмотреть по ссылке. Для доступа к стеку NodeJS + Redis файл dotcloud.yml может иметь следующий вид:

www:
  type: nodejs

data:
  type: redis

Если приложение на NodeJS использует дополнительные пакеты, то можно использовать файл package.json, в котором указать зависимости от других пакетов. Пакеты будут автоматически установлены при развертывании. Пример файла package.json:

{
  "engines": {
    "node": ">=v0.4.5"
  },
  "dependencies": {
    "redis": ">=0.6.6",
    "express": ">=2.4.2"
  }
}

Для автоматического запуска приложений на NodeJS необходимо создать третий файл с названием supervisord.conf и указать в нем путь запуска

[program:node]
command = node index.js
directory = /home/dotcloud/current

Использование environment.json

Получить доступ к стеку сервисов которые были указаны в файле dotcloud.yml очень просто. После развертывания приложения к домашней директории создается файл environment.json, содержащий информацию для доступа к сервисам. Используя этот файл можно настроить приложение для автоматического использования настроек к базам данных не заботясь о ручной настройке. Поскольку я указал Redis в качестве базы данных в файле dotcloud.yml и назвал его data, то для получения настроек к Redis из приложения на NodeJS достаточно написать:

var envfilepath = '/home/dotcloud/environment.json',
    environment = JSON.parse(require('fs').readFileSync(envfilepath));

var host = environment['DOTCLOUD_DATA_REDIS_HOST'],
    port = environment['DOTCLOUD_DATA_REDIS_PORT'],
    pass = environment['DOTCLOUD_DATA_REDIS_PASSWORD'];

Развертывание

Для создания приложения на Dotcloud необходимо написать в консоле:

dotcloud create appname

appname - в данном случае имя приложения, которое может быть любым. Ну и непосредственно пуш приложения на Dotcloud

dotcloud push appname ~/path-to-node-app/

При успешном деплое выдадут www адрес для доступа к приложению

Deployment finished successfully. Your application is available at the following URLs
www: http://d07c100d.dotcloud.com/

Что еще можно сделать

Использование собственного доменного имени. Для этого надо прописать DNS запись для соответствующего домена:

CNAME gateway.dotcloud.com.

И добавить алиас к своему приложению на Dotcloud:

dotcloud alias add appname.www www.example.com

Также можно подключится по SSH

dotcloud ssh appname.www

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

Сокращатель ссылок на Node и Redis

Началось с того, что я решил попробовать Redis в каком-нить проекте, а поскольку давно планировал сделать собственную сокращалку ссылок, то решил именно ее и написать. Связка NodeJS + Redis вообще показалась наиболее легко реализуемой. Закончилось тем что помимо Redis написал еще и реализацию для MongoDB.

Не буду вдаваться в подробности сравнения MongoDB и Redis, на эту тему итак уже написано куча статей (Comparing MongoDB And Redis Part 1 и Part 2. Для реализации сокращалки Redis’а хватает за глаза, он вообще более дружественен и прост в реализации к такого рода задачам.

Скачать мой Node Url Shortener можно на Github.

Для установки надо подтянуть дополнительные пакеты из npm. Прежде всего это ExpressJS, а также пакет для работы с Redis или MongoDB.

git clone https://github.com/dotzero/node-url-shortener nus
cd nus
npm install

Далее надо отредактировать файл ./config.js и запустить app.js через NodeJS. Планирую пользовать исключительно через API, поэтому с веб-интерфейсом особо не заморачивался. Реализация API почти такая же как у goo.gl.

Для сокращения ссылок:

GET /api/v1/shorten/?long_url=http://www.google.com

JSON ответ

{
  status_code: 200
  status_txt: "OK"
  hash: "Mw"
  url: "http://localhost/Mw"
  long_url: "http://www.google.com"
}

И для разворачивания коротких ссылок

GET /api/v1/expand/?short_url=http://localhost/Mw

Исправление ошибок при установке Redmine 1.2 на Ubuntu Server 11.04

Как я убедился на собственном опыте, большинство мануалов по установке Redmine 1.2 являются просто копипастом старых мануалов, в которых меняют номера версий. Из всех просмотренных мною статей, больше всего мне понравилась статья Manage Projects with Redmine on Ubuntu 11.04. Но даже используя ее у меня возникло пару ошибок, поэтому захотелось сделать этот мини howto.

При попытке миграции базы данных, вылетает ошибка:

rake/rdoctask is deprecated.  Use rdoc/task instead (in RDoc 2.4.2+)

Связано с тем что по-умолчанию ставится более новая версия Rake. Для исправления можно удалить свою версию (посмотреть версию rake –version) и поставить принудительно версию 0.8.7 которая подходит для RubyOnRails 2.3.11

gem uninstall -v=0.9.2 rake
gem install -v=0.8.7 rake

Следующая ошибка которая у меня возникла. Ошибка в методе version_requirements

undefined local variable or method `version_requirements'' for #<:gemdependency:0x7face0b79690>

Чтобы это исправить надо отредактировать файл /config/environment.rb. Найти в начале файла строки:

# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), ''boot'')

И дописать сразу после них условие

if Gem::VERSION >= "1.3.6"
    module Rails
        class GemDependency
            def requirement
                r = super
                (r == Gem::Requirement.default) ? nil : r
            end
        end
    end
end

Других проблем при установке у меня не возникло.