Как не нужно делать блоговые клиенты

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

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

По этой же причине невозможно работать с плагинами кросспостинга в другие сервисы – движок просто не знает, что новые записи появляются. Как выход – необходимо переписать скрипт для публикации через электронную почту, либо через XML-RPC..

Рекурсивная функция получения содержимого папки

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

// ------------ lixlpixel recursive PHP functions -------------
// scan_directory_recursively( directory to scan, filter )
// expects path to directory and optional an extension to filter
// of course PHP has to have the permissions to read the directory
// you specify and all files and folders inside this directory
// ------------------------------------------------------------

// to use this function to get all files and directories in an array, write:
//  = scan_directory_recursively('path/to/directory');

// to use this function to scan a directory and filter the results, write:
// $fileselection = scan_directory_recursively('directory', 'extension');

function scan_directory_recursively_backup($directory, $filter=FALSE)
{
    // if the path has a slash at the end we remove it here
    if(substr($directory,-1) == '/')
    {
        $directory = substr($directory,0,-1);
    }

    // if the path is not valid or is not a directory ...
    if(!file_exists($directory) || !is_dir($directory))
    {
        // ... we return false and exit the function
        return FALSE;

    // ... else if the path is readable
    }elseif(is_readable($directory))
    {
        // initialize directory tree variable
        $directory_tree = array();

        // we open the directory
        $directory_list = opendir($directory);

        // and scan through the items inside
        while (FALSE !== ($file = readdir($directory_list)))
        {
            // if the filepointer is not the current directory
            // or the parent directory
            if($file != '.' && $file != '..')
            {
                // we build the new path to scan
                $path = $directory.'/'.$file;

                // if the path is readable
                if(is_readable($path))
                {
                    // we split the new path by directories
                    $subdirectories = explode('/',$path);

                    // if the new path is a directory
                    if(is_dir($path))
                    {
                        // add the directory details to the file list
                        $directory_tree[] = array(
                            'path'    => $path,
                            'name'    => end($subdirectories),
                            'kind'    => 'directory',

                            // we scan the new path by calling this function
                            'content' => scan_directory_recursively($path, $filter));

                    // if the new path is a file
                    }elseif(is_file($path))
                    {
                        // get the file extension by taking everything after the last dot
                        $extension = end(explode('.',end($subdirectories)));

                        // if there is no filter set or the filter is set and matches
                        if($filter === FALSE || $filter == $extension)
                        {
                            // add the file details to the file list
                            $directory_tree[] = array(
                                'path'      => $path,
                                'name'      => end($subdirectories),
                                'extension' => $extension,
                                'size'      => filesize($path),
                                'kind'      => 'file');
                        }
                    }
                }
            }
        }
        // close the directory
        closedir($directory_list);

        // return file list
        return $directory_tree;

    // if the path is not readable ...
    }
    else {
        // ... we return false
        return FALSE;
    }
}

PHP, полезные ссылки

На ресурсах, представленных ниже можно найти множество реализаций тех либо иных функциональностей, DRY так сказать или “не изобретай велосипед” :)

  • http://snipplr.com/all/language/PHP
  • http://www.phpclasses.org/
  • http://www.codesphp.com/php-category

upd: сделал валидную rss ленту и прикрутил кросспостинг в Livejournal.com по расписанию. На очереди – кросспостинг в tumblr и массовый аплоад галерей с локальной машины.

Функция генерации валидного RSS

Написал функцию, генерирующую полностью валидный rss.xml
Константы берутся из конфига сайта. В rss ленту вставляется превью, помимо текста.

function rssToFile()
{
        $rssOutput = '< ?xml version="1.0" encoding="UTF-8"?>
        <rss version="2.0"
        xmlns:content="http://purl.org/rss/1.0/modules/content/"
        xmlns:wfw="http://wellformedweb.org/CommentAPI/"
        xmlns:dc="http://purl.org/dc/elements/1.1/"
        xmlns:atom="http://www.w3.org/2005/Atom"
        xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
        xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
        >
        <channel>
        <title>' . _SiteNameRu_ . '</title>
        <atom:link href="' . _BaseUrl_ . 'rss.xml" rel="self" type="application/rss+xml" />
        <link>'. _BaseUrl_ .'</link>
        <description>'. _SiteDescription_ .'</description>
        <copyright>Copyright 2011, '. _SiteName_ .'</copyright>
        <sy:updateperiod>hourly</sy:updateperiod>
        <sy:updatefrequency>10</sy:updatefrequency>';

        startup();
        $sQuery = "SELECT * FROM `Gallery` WHERE `gallery_status` = 'approved' ORDER BY `add_date` DESC LIMIT 10";
        $ResultSelect = mysql_query($sQuery) or die(mysql_error());

        while ($row = mysql_fetch_array($ResultSelect))
        {
            $id=$row['gallery_id'];
            $title=$row['gallery_seo_title'];
            $text=$row['gallery_seo_description'];
            $description = $text . "<br /><img src=\"". _BaseUrl_ . "gallery/thumbs150/" . $id . "/0.jpg\" width=\"150\"/><br />";

            $date=$row['add_date'];
            date_default_timezone_set('Europe/Berlin');
            $date=date("r", strtotime($date)); //. " GMT";

        $rssOutput .= "<item><title>$title</title>";
        $rssOutput .= "<description>< ![CDATA[". $description. "]]></description>
                <guid>" . _BaseUrl_ . "gallery/" . $id . "</guid>
                <pubdate>$date</pubdate>
                </item>";
        }
    $rssOutput .= '</channel></rss>';
    mysql_close();

    $fp=fopen('rss.xml', 'w+');

    if (!$fp) { echo "unable to open rss.xml"; }
    else
    {
        fwrite($fp, $rssOutput);
        fclose($fp);
    }

}

Работа в PHP с phpthumbs

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

function ImageResize2($type, $sFullSizeFile, $FileOutput)
{
    require_once('phpThumbs/phpthumb.class.php');
    $phpThumb = new phpThumb();

    $file = $sFullSizeFile;
    $phpThumb->setSourceFilename($file); 

    if($type == 0)
    {
        $phpThumb->setParameter('w', 150); // по ширине 150 пикселей
        $phpThumb->setParameter('zc', 1);  // делаем кроп по центру
    }

    if($type == 1)
    {
        $phpThumb->setParameter('w', 300);
    }

    if($type == 2)
    {
        $phpThumb->setParameter('w', 800); // по ширине 800 пикселей
        $phpThumb->setParameter('aoe', 0); // только уменьшаем!
    }    

    $outputFilename = $FileOutput;

    if ($phpThumb->GenerateThumbnail())
    {
        if ($phpThumb->RenderToFile($outputFilename))
        {
            // echo 'Success<br />';
        }
        else
        {
            // echo 'RenderToFile: failed<br />';
        }
    }
    else
    {
        // echo 'GenerateThumbnail: failed<br />';
    }
}

PHP: Функция рекурсивного удаления директории

Как аргумент передается полный путь к директории, к примеру “/var/www/site.ru/temp”

function deleteDirectory($dir)
{
    if (!file_exists($dir)) return true;
     if (!is_dir($dir) || is_link($dir)) return unlink($dir);
        foreach (scandir($dir) as $item) {
            if ($item == '.' || $item == '..') continue;
            if (!deleteDirectory($dir . "/" . $item)) {
                chmod($dir . "/" . $item, 0777);
                if (!deleteDirectory($dir . "/" . $item)) return false;
            };
        }
    return rmdir($dir);
}

PHP: Функция получения страницы сайта

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

function get_host($s)
{
    $s = preg_replace('#^http://#Uis', '', trim($s));
    $s = explode('/', trim($s));
    $s = trim($s[0]);
    $s = explode(':', $s);
    $s = trim($s[0]);
    return $s;
}

function get_web_page( $url )
{
    $uagent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)";
    $sRefferal = 'http://' . get_host($url);
    $ch = curl_init( $url );
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);   // возвращает веб-страницу

    curl_setopt($ch, CURLOPT_REFERER, $sRefferal);

    curl_setopt($ch, CURLOPT_HEADER, 0);           // не возвращает заголовки
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);   // переходит по редиректам
    curl_setopt($ch, CURLOPT_ENCODING, "");        // обрабатывает все кодировки
    curl_setopt($ch, CURLOPT_USERAGENT, $uagent);  // useragent
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120); // таймаут соединения
    curl_setopt($ch, CURLOPT_TIMEOUT, 120);        // таймаут ответа
    curl_setopt($ch, CURLOPT_MAXREDIRS, 10);       // останавливаться после 10-ого редиректа

    $content = curl_exec( $ch );
    $err     = curl_errno( $ch );
    $errmsg  = curl_error( $ch );
    $header  = curl_getinfo( $ch );
    curl_close( $ch );

    $header['errno']   = $err;
    $header['errmsg']  = $errmsg;
    $header['content'] = $content;
    return $header;
}

Работа со Sphinx

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

  • при установке на Debian оказалось, что не хватает пакета libmysql++-dev
  • долго бился с тем, что sphinx не может подключиться к базе данных. Оказалось пароль содержит “#” который в конфиге воспринимается как комментарий
  • - долго чистил конфиг сфинкса пока понял что и куда там :) почти всё обрезал, оставив самое необходимое(на данный момент)

  • понял, как настроить несколько индексов для использования одного сфинкса для нескольких сайтов – мега-удобно!
  • долго пытался понять и лишь частично решил выбор индекса(решил) и установку фильтров(так и не решил. причем ни на включение ни на exclude результатов) при работе с PHP. Решил костылями.
  • подключил sphinx к поиску “похожих” постов. зачем мучаться, делать выборки через like и т.д. в базе, когда рядом стоит такой монстрик, как sphinx? :) дописал функцию, добавил входящих параметров – работает во всех случаях. Супер!

Небольшая подборка полезных ссылок

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

Скрипт для раскидывания изображений по папкам

Возникла необходимость создать 100 папок и в каждую из них переместить по одному изображению. Вот реализация на php при работе с Denwer:

< ?php

// базовая директория, в которой будем работать
$sBasePath = '/home/localhost/www/!!!/';
// сканируем заданную папку и имена файлов добавляем в массив
$aDirContent = scandir($basePath . 'images/');
// создаем 100 папок и перемещаем по одному файлу в папку
for($counter = 1; $counter < 101; $counter++ )
{
    // создаем папку
    mkdir($aBasePath . $counter, 0777);
    // нулевой и первый элементы массива это текущая и родительская директории
    $sFileName = $aDirContent[$counter+1];
    // откуда брать файл
    $sWhere = $sBasePath . $counter . '/' . $sFileName;
    // куда ложить файл
    $sFrom = $sBasePath . 'images/' . $sFileName;
    copy($sFrom, $sWhere);
    // удаляем файл (функция перемещения это копирование+удаление)
    unlink($sFrom);
}
?>