Jacek Kowalski
2013-09-05 0868e0642f694bf5c08951f67f5a4b7eadde041a
Zmiana numeru wersji na 2.6, testy jednostkowe niektórych klas (PHPUnit),
dodanych wiele komentarzy dotyczących metod oraz atrybutów (Doxygen),
aktualizacja instrukcji instalacji i aktualizacji.
15 files modified
8 files added
893 ■■■■■ changed files
.travis.yml 8 ●●●●● patch | view | raw | blame | history
Doxyfile 285 ●●●●● patch | view | raw | blame | history
INSTALL 7 ●●●●● patch | view | raw | blame | history
UPGRADE 11 ●●●●● patch | view | raw | blame | history
class/BotImage.php 17 ●●●●● patch | view | raw | blame | history
class/BotImageGG.php 2 ●●● patch | view | raw | blame | history
class/BotMessage.php 31 ●●●● patch | view | raw | blame | history
class/BotModule.php 40 ●●●●● patch | view | raw | blame | history
class/BotMsg.php 46 ●●●● patch | view | raw | blame | history
class/BotSession.php 73 ●●●● patch | view | raw | blame | history
class/BotUser.php 21 ●●●● patch | view | raw | blame | history
class/legacy/calendar.php 6 ●●●●● patch | view | raw | blame | history
class/legacy/funcs.php 13 ●●●●● patch | view | raw | blame | history
class/legacy/jsarray.php 15 ●●●● patch | view | raw | blame | history
class/legacy/main.php 10 ●●●● patch | view | raw | blame | history
class/legacy/module.php 15 ●●●● patch | view | raw | blame | history
class/std.php 4 ●●● patch | view | raw | blame | history
phpunit.xml 10 ●●●●● patch | view | raw | blame | history
tests/Core/BotMessageTest.php 13 ●●●●● patch | view | raw | blame | history
tests/Core/BotMsgTest.php 99 ●●●●● patch | view | raw | blame | history
tests/Core/BotSessionTest.php 136 ●●●●● patch | view | raw | blame | history
tests/Core/Legacy/JSArrayTest.php 29 ●●●●● patch | view | raw | blame | history
tests/autoload.php 2 ●●●●● patch | view | raw | blame | history
.travis.yml
New file
@@ -0,0 +1,8 @@
language: php
php:
  - 5.5
  - 5.4
  - 5.3
  - 5.2
script: phpunit --coverage-text
Doxyfile
New file
@@ -0,0 +1,285 @@
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING      = UTF-8
PROJECT_NAME           = "Bot Gadu-Gadu"
PROJECT_NUMBER         =
OUTPUT_DIRECTORY       = docs
CREATE_SUBDIRS         = NO
OUTPUT_LANGUAGE        = Polish
BRIEF_MEMBER_DESC      = YES
REPEAT_BRIEF           = YES
ABBREVIATE_BRIEF       = "The $name class" \
                         "The $name widget" \
                         "The $name file" \
                         is \
                         provides \
                         specifies \
                         contains \
                         represents \
                         a \
                         an \
                         the
ALWAYS_DETAILED_SEC    = NO
INLINE_INHERITED_MEMB  = NO
FULL_PATH_NAMES        = YES
STRIP_FROM_PATH        =
STRIP_FROM_INC_PATH    =
SHORT_NAMES            = NO
JAVADOC_AUTOBRIEF      = NO
QT_AUTOBRIEF           = NO
MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS           = YES
SEPARATE_MEMBER_PAGES  = NO
TAB_SIZE               = 8
ALIASES                =
OPTIMIZE_OUTPUT_FOR_C  = NO
OPTIMIZE_OUTPUT_JAVA   = NO
OPTIMIZE_FOR_FORTRAN   = NO
OPTIMIZE_OUTPUT_VHDL   = NO
EXTENSION_MAPPING      =
BUILTIN_STL_SUPPORT    = NO
CPP_CLI_SUPPORT        = NO
SIP_SUPPORT            = NO
IDL_PROPERTY_SUPPORT   = YES
DISTRIBUTE_GROUP_DOC   = NO
SUBGROUPING            = YES
TYPEDEF_HIDES_STRUCT   = NO
SYMBOL_CACHE_SIZE      = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL            = NO
EXTRACT_PRIVATE        = YES
EXTRACT_STATIC         = YES
EXTRACT_LOCAL_CLASSES  = YES
EXTRACT_LOCAL_METHODS  = YES
EXTRACT_ANON_NSPACES   = NO
HIDE_UNDOC_MEMBERS     = NO
HIDE_UNDOC_CLASSES     = NO
HIDE_FRIEND_COMPOUNDS  = NO
HIDE_IN_BODY_DOCS      = NO
INTERNAL_DOCS          = NO
CASE_SENSE_NAMES       = NO
HIDE_SCOPE_NAMES       = NO
SHOW_INCLUDE_FILES     = YES
FORCE_LOCAL_INCLUDES   = NO
INLINE_INFO            = YES
SORT_MEMBER_DOCS       = YES
SORT_BRIEF_DOCS        = NO
SORT_MEMBERS_CTORS_1ST = NO
SORT_GROUP_NAMES       = NO
SORT_BY_SCOPE_NAME     = NO
GENERATE_TODOLIST      = YES
GENERATE_TESTLIST      = YES
GENERATE_BUGLIST       = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS       =
MAX_INITIALIZER_LINES  = 30
SHOW_USED_FILES        = YES
SHOW_DIRECTORIES       = NO
SHOW_FILES             = YES
SHOW_NAMESPACES        = YES
FILE_VERSION_FILTER    =
LAYOUT_FILE            =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET                  = NO
WARNINGS               = YES
WARN_IF_UNDOCUMENTED   = YES
WARN_IF_DOC_ERROR      = YES
WARN_NO_PARAMDOC       = NO
WARN_FORMAT            = "$file:$line: $text"
WARN_LOGFILE           =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT                  =
INPUT_ENCODING         = UTF-8
FILE_PATTERNS          = *.php
RECURSIVE              = YES
EXCLUDE                =
EXCLUDE_SYMLINKS       = NO
EXCLUDE_PATTERNS       = */docs/* */data/* */tests/* */modules/*
EXCLUDE_SYMBOLS        =
EXAMPLE_PATH           =
EXAMPLE_PATTERNS       = *
EXAMPLE_RECURSIVE      = NO
IMAGE_PATH             =
INPUT_FILTER           =
FILTER_PATTERNS        =
FILTER_SOURCE_FILES    = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER         = NO
INLINE_SOURCES         = NO
STRIP_CODE_COMMENTS    = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION    = NO
REFERENCES_LINK_SOURCE = YES
USE_HTAGS              = NO
VERBATIM_HEADERS       = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX     = NO
COLS_IN_ALPHA_INDEX    = 5
IGNORE_PREFIX          =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML          = YES
HTML_OUTPUT            = html
HTML_FILE_EXTENSION    = .html
HTML_HEADER            =
HTML_FOOTER            =
HTML_STYLESHEET        =
HTML_TIMESTAMP         = YES
HTML_ALIGN_MEMBERS     = YES
HTML_DYNAMIC_SECTIONS  = NO
GENERATE_DOCSET        = NO
DOCSET_FEEDNAME        = "Doxygen generated docs"
DOCSET_BUNDLE_ID       = org.doxygen.Project
GENERATE_HTMLHELP      = NO
CHM_FILE               =
HHC_LOCATION           =
GENERATE_CHI           = NO
CHM_INDEX_ENCODING     =
BINARY_TOC             = NO
TOC_EXPAND             = NO
GENERATE_QHP           = NO
QCH_FILE               =
QHP_NAMESPACE          = org.doxygen.Project
QHP_VIRTUAL_FOLDER     = doc
QHP_CUST_FILTER_NAME   =
QHP_CUST_FILTER_ATTRS  =
QHP_SECT_FILTER_ATTRS  =
QHG_LOCATION           =
GENERATE_ECLIPSEHELP   = NO
ECLIPSE_DOC_ID         = org.doxygen.Project
DISABLE_INDEX          = NO
ENUM_VALUES_PER_LINE   = 4
GENERATE_TREEVIEW      = NO
USE_INLINE_TREES       = NO
TREEVIEW_WIDTH         = 250
FORMULA_FONTSIZE       = 10
SEARCHENGINE           = YES
SERVER_BASED_SEARCH    = NO
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX         = YES
LATEX_OUTPUT           = latex
LATEX_CMD_NAME         = latex
MAKEINDEX_CMD_NAME     = makeindex
COMPACT_LATEX          = NO
PAPER_TYPE             = a4wide
EXTRA_PACKAGES         =
LATEX_HEADER           =
PDF_HYPERLINKS         = YES
USE_PDFLATEX           = YES
LATEX_BATCHMODE        = NO
LATEX_HIDE_INDICES     = NO
LATEX_SOURCE_CODE      = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF           = NO
RTF_OUTPUT             = rtf
COMPACT_RTF            = NO
RTF_HYPERLINKS         = NO
RTF_STYLESHEET_FILE    =
RTF_EXTENSIONS_FILE    =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN           = NO
MAN_OUTPUT             = man
MAN_EXTENSION          = .3
MAN_LINKS              = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML           = NO
XML_OUTPUT             = xml
XML_SCHEMA             =
XML_DTD                =
XML_PROGRAMLISTING     = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF   = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD       = NO
PERLMOD_LATEX          = NO
PERLMOD_PRETTY         = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING   = YES
MACRO_EXPANSION        = NO
EXPAND_ONLY_PREDEF     = NO
SEARCH_INCLUDES        = YES
INCLUDE_PATH           =
INCLUDE_FILE_PATTERNS  =
PREDEFINED             =
EXPAND_AS_DEFINED      =
SKIP_FUNCTION_MACROS   = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES               =
GENERATE_TAGFILE       =
ALLEXTERNALS           = NO
EXTERNAL_GROUPS        = YES
PERL_PATH              = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS         = YES
MSCGEN_PATH            =
HIDE_UNDOC_RELATIONS   = YES
HAVE_DOT               = NO
DOT_FONTNAME           = FreeSans
DOT_FONTSIZE           = 10
DOT_FONTPATH           =
CLASS_GRAPH            = YES
COLLABORATION_GRAPH    = YES
GROUP_GRAPHS           = YES
UML_LOOK               = NO
TEMPLATE_RELATIONS     = NO
INCLUDE_GRAPH          = YES
INCLUDED_BY_GRAPH      = YES
CALL_GRAPH             = NO
CALLER_GRAPH           = NO
GRAPHICAL_HIERARCHY    = YES
DIRECTORY_GRAPH        = YES
DOT_IMAGE_FORMAT       = png
DOT_PATH               =
DOTFILE_DIRS           =
DOT_GRAPH_MAX_NODES    = 50
MAX_DOT_GRAPH_DEPTH    = 0
DOT_TRANSPARENT        = NO
DOT_MULTI_TARGETS      = NO
GENERATE_LEGEND        = YES
DOT_CLEANUP            = YES
INSTALL
@@ -8,8 +8,6 @@
    http://dev.gg.pl/api/pages/botapi.html#rejestracja
* ustaw serwer WWW tak, by plik BotGG.php był wykonywany po wejściu na
  ustalony przy rejestracji adres
* ustaw serwer WWW tak, by plik BotIMI.php był wykonywany po wejściu na
  ustalony w IMified.com adres
* zmień /sciezka/do/bota w pliku data/update.sh na rzeczywistą, bezwzględną
  ścieżkę do katalogu skryptu
* wykonuj za pomocą crona polecenie `/sciezka/do/bota/data/update.sh` ok.
@@ -34,12 +32,13 @@
-------
Jeśli dodajesz/usuwasz moduły lub komendy KONIECZNIE usuń wszystkie pliki
z katalogu ./cache - w przeciwnym wypadku polecenia te mogą powodować błędy
w działaniu bota!
z katalogu ./cache - w przeciwnym wypadku może to powodować błędy
w działaniu całego bota!
------------
 INFORMACJA
------------
W razie problemów zacznij od zmiany
    error_reporting(E_COMPILE_ERROR|E_PARSE);
na
UPGRADE
@@ -1,3 +1,14 @@
Wersję można sprawdzić w pliku ./class/legacy/main.php
===========================
 AKTUALIZACJA Z WERSJI 2.5
===========================
* nadpisz wszystkie pliki w katalogu ./class (oraz podkatalogach),
  za wyjątkiem ./class/config.php
* zastąp plik ./data/lotto/pobierz.php
* zastąp plik ./data/tv/wp_parse.php
===========================
 AKTUALIZACJA Z WERSJI 2.4
===========================
class/BotImage.php
@@ -1,7 +1,24 @@
<?php
/**
 * Interfejs do obsługi obrazków przychodzących
 */
abstract class BotImage {
    /**
     * Zmienna przechowująca zasób biblioteki GD2 z obrazkiem
     * @var resource $data
     */
    protected $data = NULL;
    /**
     * Funkcja zwracająca zasób GD2 z obrazkiem
     * @return resource Zasób GD2
     */
    abstract function getImage();
    /**
     * Funkcja zwracająca dane obrazka w formie ciągu bajtów.
     * @return string Obrazek
     */
    abstract function getImageData();
}
?>
class/BotImageGG.php
@@ -16,7 +16,7 @@
    
    function getImageData() {
        if($this->data === NULL) {
            $push = new BotGGAPI();
            $push = new BotAPIGG();
            $this->data = $push->getImage($this->hash);
        }
        
class/BotMessage.php
@@ -2,58 +2,71 @@
class BotMessage {
    /**
     * Informacje o kliencie
     * @var BotUser
     * @var BotUser $user
     */
    protected $user;
    /**
     * Informacje o kliencie zgodne z poprzednią wersją Bota (dot. API IMified).
     * Najczęściej równe {@link BotMessage::$user}
     * @var BotUser
     * @var BotUser $userAlt
     */
    protected $userAlt;
    
    /**
     * Sesja przypisana do użytkownika i modułu
     * @var BotSession
     * @var BotSession $session
     */
    protected $session;
    
    /**
     * Tekst otrzymany od API - bez zmian
     * @var string
     * @var string $rawText
     */
    protected $rawText;
    
    /**
     * Czysty tekst, tylko znaki ASCII, małe litery, podwójne spacje zamienione na pojedyncze
     * @var string
     * @var string $text
     */
    protected $text;
    
    /**
     * Tablica obrazków (zobacz klasę BotImage) przesłanych do bota przez użytkownika.
     * @var array
     * Tablica obrazków (zobacz {@link BotImage}) przesłanych do bota przez użytkownika.
     * @var array $images
     */
    protected $images = array();
    
    /**
     * Komenda, tylko znaki ASCII, małe litery
     * @var string
     * @var string $command
     */
    private $command;
    
    /**
     * Argumenty polecenia - oryginalne
     * @var string
     * @var string $args
     */
    private $args;
    
    /**
     * Umożliwia dostęp tylko do odczytu do prywanych zmiennych
     * @param string $name Nazwa zmiennej
     * @return mixed Wartość zmiennej prywatnej
     */
    function __get($name) {
        return $this->$name;
    }
    
    /**
     * Na podstawie nieprzetworzonej wiadomości ({@link BotMessage::$rawText})
     * metoda ustawia wszystkie pola klasy.
     * @param string $value Nieprzetworzona wiadomość
     */
    function setText($value) {
        $this->rawText = $value;
        $value = trim($value);
        $this->text = funcs::utfToAscii($value);
        $this->command = funcs::utfToAscii(trim(strtok($value, " \t\r\n")));
        $this->args = trim(strtok(''));
class/BotModule.php
@@ -1,11 +1,47 @@
<?php
class BotModuleException extends Exception {}
interface BotModule {
}
interface BotModule {}
/**
 * Interfejs klasy inicjującej moduł bota
 */
interface BotModuleInit {
    /**
     * Funkcja zwracająca listę obsługiwanych komend.
     * Przykład:
     * <pre>array(
     *   'komenda' => array(
     *     array(
     *       'file' => 'komenda.php',
     *       'class' => 'bot_NAZWAMODULU_module',
     *       'method' => 'komenda1',
     *       'params' => 'parametr_do_funkcji',
     *     ),
     *     array(
     *       'file' => 'komenda.php',
     *       'class' => 'bot_NAZWAMODULU_module',
     *       'method' => 'komenda2',
     *     ),
     *   ),
     *   '*' => array(
     *     array(
     *       'file' => 'test.php',
     *       'class' => 'NAZWAMODULU_test',
     *       'method' => 'komenda_test',
     *     ),
     *   ),
     * )</pre>
     * @return array Lista obsługiwanych komend
     */
    function register();
    /**
     * Zwraca pomoc dla polecenia lub skróconą listę poleceń
     * obsługiwanych przez dany moduł.
     * @param string|NULL $params Nazwa komendy
     * @return BotMsg Pomoc dla komendy
     */
    function help($params = NULL);
}
?>
class/BotMsg.php
@@ -1,11 +1,33 @@
<?php
class BotMsgException extends Exception {}
/**
 * Interfejs dla klas przetwarzających wiadomości wychodzące
 * do formatu właściwego dla danej sieci.
 */
interface BotMsgInterface {
    /**
     * Konstruktor
     * @param BotMsg $msg Wiadomość do przetworzenia
     */
    function __construct(BotMsg $msg);
    /**
     * Zwraca przetworzoną wiadomość
     * @return string Wiadomość po przetworzeniu
     */
    function __toString();
    /**
     * Podaje na wyjście (np. za pomocą echo) wiadomość w formacie
     * odpowiednim dla danego API, uwzględniając nagłówki HTTP
     * i inne konieczne elementy.
     */
    function sendPullResponse();
}
/**
 * Klasa reprezentująca wiadomość wychodzącą.
 */
class BotMsg {
    private $beautiful = TRUE;
    private $parser = NULL;
@@ -14,14 +36,28 @@
    private $raw = '';
    
    /**
     * Włącza lub wyłącza "upiększanie" konwertowanej do czystego tekstu wiadomości, np.:
     * <b>abc</b> zamieniane jest na *abc*
     * <h1>efg</h1> przechodzi w = efg =
     * Włącza lub wyłącza "upiększanie" konwertowanej
     * do czystego tekstu ({@link BotMsg::getText()}) wiadomości, np.:
     *
     * &lt;b&gt;abc&lt;/b&gt; zamieniane jest na \*abc\*
     *
     * &lt;h1&gt;efg&lt;h1&gt; przechodzi w = efg =
     *
     * Domyślnie włączone
     * @param bool $set Ustawienie "upiększania"
     */
    function beautifulText($set = FALSE) {
    function setBeautiful($set = FALSE) {
        if($this->beautiful != $set) {
            $this->text = $this->html = $this->parser = NULL;
        $this->beautiful = (bool)$set;
        }
    }
    /**
     * @deprecated Zastąpiono funkcją {@link BotMsg::setBeautiful()}
     */
    function beautifulText($set = FALSE) {
        $this->setBeautiful($set);
    }
    
    /**
@@ -204,7 +240,7 @@
            }
        }
        
        return $return;
        return trim($return);
    }
    
    private function parseHTMLDOM($dom) {
class/BotSession.php
@@ -1,18 +1,29 @@
<?php
/**
 * Klasa przechowująca dane użytkownika. Całość przypomina mechanizm sesji w PHP.
 * Klasa przechowująca dane przekazane przez użytkownika,
 * w szczególności jego ustawienia.
 */
class BotSession {
    private $PDO;
    
    /**
     * Nazwa modułu, którego zmienne klasa przetwarza
     * @var string max. 40 znaków
     * @var string $class max. 40 znaków
     */
    protected $class = '';
    protected $class_empty = TRUE;
    
    /**
     * Pseudo-URL użytkownika.
     * @see BotUser
     * @var string $user URL użytkownika
     */
    private $user;
    /**
     * Klasa z identyfikatorem użytkownika
     * @var BotUser $user_struct
     */
    private $user_struct;
    
    /**
     * Inicjuje klasę w zależności od użytkownika
@@ -55,10 +66,10 @@
                $version = 1;
            }
            
            if($version < 3) {
            if($version < 4) {
                $this->PDO->query('DELETE FROM data WHERE class IS NULL AND name=\'user_struct\'');
                $this->PDO->query('INSERT OR REPLACE INTO data (class, name, value) VALUES (\'\', \'_version\', 3)');
                $version = 3;
                $this->PDO->query('INSERT OR REPLACE INTO data (class, name, value) VALUES (\'\', \'_version\', 4)');
                $version = 4;
            }
            
            return;
@@ -71,7 +82,7 @@
            
            $this->PDO->query(
                'CREATE TABLE data (
                    class VARCHAR(50),
                    class VARCHAR(50) NOT NULL DEFAULT \'\',
                    name VARCHAR(40) NOT NULL,
                    value TEXT NOT NULL,
                    PRIMARY KEY (
@@ -81,6 +92,8 @@
                )'
            );
            
            $this->PDO->query('INSERT INTO data (class, name, value) VALUES (\'\', \'_version\', 4)');
            $files = glob(BOT_TOPDIR.'/db/*/'.$this->user_struct['user'].'.ggdb');
            if(!$files) {
                return;
@@ -89,12 +102,10 @@
            $this->PDO->beginTransaction();
            $st = $this->PDO->prepare('INSERT OR REPLACE INTO data (class, name, value) VALUES (?, ?, ?)');
            
            $st->execute(array('', '_version', 2));
            foreach($files as $file) {
                $data = unserialize(file_get_contents($file));
                foreach($data as $name => $value) {
                    $st->execute(array($this->class, $name, $value));
                    $st->execute(array($this->class, $name, serialize($value)));
                }
            }
            
@@ -112,6 +123,19 @@
        }
    }
    
    /**
     * Ustawia nazwę modułu/klasy, której zmienne będą przetwarzane
     * @param string $class Nazwa modułu
     */
    function setClass($class) {
        $this->class = $class;
    }
    /**
     * Pobiera zmienną modułu o podanej nazwie (getter).
     * @param string $name Nazwa zmiennej
     * @return mixed Wartość zmiennej lub NULL
     */
    function __get($name) {
        $this->init();
        
@@ -128,6 +152,11 @@
        }
    }
    
    /**
     * Ustawia zmienną o podanej nazwie
     * @param string $name Nazwa zmiennej
     * @param mixed $value Wartość zmiennej
     */
    function __set($name, $value) {
        $this->init();
        
@@ -135,6 +164,11 @@
        $st->execute(array($this->class, $name, serialize($value)));
    }
    
    /**
     * Sprawdza czy podana zmienna została ustawiona.
     * @param string $name Nazwa zmiennej
     * @return bool Czy zmienna istnieje?
     */
    function __isset($name) {
        $this->init();
        
@@ -145,6 +179,10 @@
        return ($st[0]>0);
    }
    
    /**
     * Usuwa zmienną o podanej nazwie
     * @param string $name Nazwa zmiennej
     */
    function __unset($name) {
        $this->init();
        
@@ -152,6 +190,10 @@
        $st->execute(array($this->class, $name));
    }
    
    /**
     * Zapamiętuje tablicę zmiennych danego modułu
     * @param array $array Tablica zmiennych
     */
    function push($array) {
        $this->PDO->beginTransaction();
        foreach($array as $name => $value) {
@@ -160,6 +202,10 @@
        $this->PDO->commit();
    }
    
    /**
     * Zwraca wszystkie ustawione zmienne danego modułu
     * @return array Lista wszystkich zmiennych
     */
    function pull() {
        $this->init();
        
@@ -169,16 +215,15 @@
        
        $return = array();
        foreach($st as $row) {
            $return[$row['name']] = $row['value'];
            $return[$row['name']] = unserialize($row['value']);
        }
        
        return $return;
    }
    
    function setClass($class) {
        $this->class = $class;
    }
    /**
     * Usuwa wszystkie zmienne sesyjne danego modułu.
     */
    function truncate() {
        $this->init();
        
class/BotUser.php
@@ -9,13 +9,13 @@
     * - IMified
     * - HTTP
     * - Local
     * @var string
     * @var string $interface
     */
    private $interface;
    
    /**
     * Numer lub identyfikator użytkownika
     * @var string
     * @var string $uid
     */
    private $uid;
    
@@ -29,22 +29,30 @@
     * - yahoo.imified.com
     * - gtalk.imified.com
     * - localhost
     * @var string
     * @var string $network
     */
    private $network;
    
    /**
     * Identyfikator/unikalna nazwa bota, do którego skierowano zapytanie.
     * Najczęściej numer Gadu-Gadu lub botkey w przypadku IMified.com
     * @var string $bot
     */
    private $bot;
    
    /**
     * Parametry zapytania. Przy IMified równe zmiennej $_POST['channel']
     * @var string
     * @var string $params
     */
    private $params;
    
    /**
     * Konstruktor. W argumencie otrzymuje pseudo-URL określający użytkownika i sieć.
     * Przykłady:
     * - Gadu-Gadu://123456\@gadu-gadu.pl
     * - IMified://user\\\@jabber\@jabber.imified.com/BOTKEY?private
     * @param string $user URL użytkownika
     */
    function __construct($user) {
        $data = parse_url($user);
        
@@ -55,6 +63,11 @@
        $this->params = @$data['query'];
    }
    
    /**
     * Umożliwia dostęp tylko do odczytu do prywanych zmiennych
     * @param string $name Nazwa zmiennej
     * @return mixed Wartość zmiennej prywatnej
     */
    function __get($name) {
        return $this->$name;
    }
class/legacy/calendar.php
@@ -1,12 +1,14 @@
<?php
/**
 * Klasa obsługująca polskie wyrażenia określające daty
 * @todo Potrzebna jest funkcja, które będzie wyciągać datę z początku lub końca danego ciągu. Patrz: {@link tv::parse_date()}
 */
class calendar {
    /**
     * Na podstawie podanej daty zwraca uniksowy znacznik czasu
     * @param string $date Data w formacie "naturalnym". Np. jutro, 1 stycznia, piątek
     * @return int Uniksowy znacznik czasu. Do użycia w funkcji date()
     * @uses funcs::utfToAscii()
     * @return int Uniksowy znacznik czasu - liczba sekund od północy 1 stycznia 1970
     * @see http://www.php.net/date
     */
    static function parse_date($date) {
        $date = funcs::utfToAscii($date);
class/legacy/funcs.php
@@ -2,13 +2,24 @@
class BotLegacyEnd extends Exception {}
class funcs {
    /**
     * Przerywa dalsze wykonywanie modułu i wysyła odpowiedź do klienta
     * @deprecated Przestarzałe wraz z wprowadzeniem nowego API.
     *  Metoda może zostać usunięta bez ostrzeżenia!
     */
    static function end() {
        throw new BotLegacyEnd();
    }
    
    /**
     * Funkcja usuwa "ogonki" (transliteracja), podwójne spacje i odstępy
     * z podanego w parametrze ciągu znaków oraz zamienia wszystkie litery na małe
     * @param string $utf Ciąg znaków w UTF-8
     * @returns string Ciąg po przetworzeniu
     */
    static function utfToAscii($utf) {
        $utf = trim(str_replace('  ', ' ', $utf));
        return strtolower(iconv('utf-8', 'ascii//TRANSLIT', trim($utf)));
        return strtolower(iconv('utf-8', 'ascii//TRANSLIT', $utf));
    }
}
?>
class/legacy/jsarray.php
@@ -11,8 +11,11 @@
                // Ignore  < ? php  and  ? >  added above
                if($token[0] == T_OPEN_TAG OR $token[0] == T_CLOSE_TAG) continue;
                // String/int element within an array
                if($token[0] == T_CONSTANT_ENCAPSED_STRING || $token[0] == T_LNUMBER) {
                if($token[0] == T_CONSTANT_ENCAPSED_STRING) {
                    $element = substr($token[1], 1, -1);
                }
                if($token[0] == T_LNUMBER) {
                    $element = $token[1];
                }
            }
            // Nested array
@@ -21,7 +24,7 @@
            }
            // End of nested array
            elseif($token == ']') {
                // Put elements into the lastest array
                // Put elements into the latest array
                if($element !== NULL && $element !== FALSE) {
                    end($stack);
                    $stack[key($stack)][] = $element;
@@ -38,7 +41,7 @@
            }
            // Elements separator
            elseif($token == ',') {
                // Put elements into the lastest array (]] check)
                // Put elements into the latest array (]] check)
                if($element !== FALSE) {
                    end($stack);
                    $stack[key($stack)][] = $element;
@@ -47,11 +50,15 @@
            }
            else
            {
                return array();
                return FALSE;
            }
        }
        
        if(isset($stack[0][0])) {
        return $stack[0][0];
        } else {
            return NULL;
        }
    }
}
?>
class/legacy/main.php
@@ -1,6 +1,12 @@
<?php
class main {
    const VERSION = '2.5';
    const VERSION_NUM = '2.5';
    /**
     * Wersja bota w formacie: <i>X.Y TYPE</i>, np. <i>2.0 Beta</i>
     */
    const VERSION = '2.6';
    /**
     * Wersja bota, tylko część numeryczna (X.Y), np. <i>2.0</i>
     */
    const VERSION_NUM = '2.6';
}
?>
class/legacy/module.php
@@ -1,18 +1,19 @@
<?php
interface module {
    static function register_cmd();
    // Returns:
    // Zwraca:
    // array(
    //   'CMD1_NAME' => 'FUNCTION_INSIDE_CLASS',
    //   'CMD2_NAME' => 'FUNCTION_INSIDE_CLASS',
    //   'KOMENDA1' => 'METODA_OBSLUGUJACA_KOMENDE1',
    //   'KOMENDA2' => 'METODA_OBSLUGUJACA_KOMENDE2',
    //   ...
    // )
    
    static function help($cmd=NULL);
    // Return help content about command $cmd to GGapi::put*() functions
    //    if $cmd is NULL return help content for all commands
    // Zwraca pomoc dotyczącą komendy z użyciem funkcji GGapi::put*()
    // Jeśli $cmd === NULL, zwraca skróconą listę poleceń modułu
    
    // static function FUNCTION_INSIDE_CLASS(CMD_NAME, REST_OF_PLAINTEXT)
    //    REST_OF_PLAINTEXT is raw (non trimmed etc.) part after command name, without leading space
    // static function METODA_OBSLUGUJACA_KOMENDE(NAZWA_KOMENDY, ARGUMENTY)
    //    ARGUMENTY to wszystko poza nazwą komendy, przekazane w taki sposób,
    //    w jaki zostały otrzymane od użytkownika
}
?>
class/std.php
@@ -30,11 +30,13 @@
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
if(!defined('PHPUNIT')) {
error_reporting(E_COMPILE_ERROR|E_PARSE);
    set_error_handler('errorToException', E_ALL & ~E_NOTICE);
}
setlocale(LC_CTYPE, 'pl_PL.utf8', 'pl_PL', 'polish', 'plk');
mb_internal_encoding('UTF-8');
libxml_use_internal_errors();
spl_autoload_register('botAutoload');
set_error_handler('errorToException', E_ALL & ~E_NOTICE);
?>
phpunit.xml
New file
@@ -0,0 +1,10 @@
<phpunit bootstrap="tests/autoload.php">
  <php>
    <const name="PHPUNIT" value="true" />
  </php>
  <testsuites>
    <testsuite name="Core Test Suite">
      <directory suffix="Test.php">tests/Core</directory>
    </testsuite>
  </testsuites>
</phpunit>
tests/Core/BotMessageTest.php
New file
@@ -0,0 +1,13 @@
<?php
class BotMessageTest extends PHPUnit_Framework_TestCase {
    function testAppend() {
        $input = '   ąęß  śćżń  óó ';
        $message = new BotMessage();
        $message->setText($input);
        $this->assertEquals($input, $message->rawText);
        $this->assertEquals('aess sczn oo', $message->text);
        $this->assertEquals('aess', $message->command);
        $this->assertEquals('śćżń  óó', $message->args);
    }
}
tests/Core/BotMsgTest.php
New file
@@ -0,0 +1,99 @@
<?php
class BotMsgTest extends PHPUnit_Framework_TestCase {
    function testAppend() {
        $text = '';
        $substring = 'abc';
        $msg = new BotMsg($substring);
        $text .= $substring;
        $substring = 'cba';
        $msg->a($substring);
        $text .= $substring;
        $substring = 'cba';
        $msg->append($substring);
        $text .= $substring;
        $this->assertEquals($text, $msg->getRaw());
    }
    function testBeautifilText() {
        $msg = new BotMsg('<h1>Test</h1><p><u><i>This.</i></u></p><p><b>That!</b></p>');
        $expect = '= Test ='."\n"
            .'_/This./_'."\n\n"
            .'*That!*';
        $msg->setBeautiful(TRUE);
        $this->assertEquals($expect, $msg->getText());
        $expect = 'Test'."\n"
            .'This.'."\n\n"
            .'That!';
        $msg->setBeautiful(FALSE);
        $this->assertEquals($expect, $msg->getText());
    }
    function testGetText() {
        $msg = new BotMsg('<h2>Test</h2>'."\n"
            .'<h3>Test h3</h3>'."\n"
            .'<p><a href="http://jacekk.info">http://jacekk.info</a><br />'."\n"
            .'<a href="http://jacekk.info">Jacekk.info</a></p>');
        $expect = '== Test =='."\n"
            .'=== Test h3 ==='."\n"
            .'http://jacekk.info'."\n"
            .'Jacekk.info (http://jacekk.info)';
        $this->assertEquals($expect, $msg->getText());
        $msg = new BotMsg('<table>'."\n"
            .'<tr><th>Header 1</th> <th>Header 2</th></tr>'."\n"
            .'<tr><td>Cell 1</td> <td>Cell 2<img src="" /></td></tr>'."\n"
            .'</table>');
        $expect = '*Header 1*     *Header 2*'."\n"
            .'Cell 1     Cell 2';
        $this->assertEquals($expect, $msg->getText());
        $msg = new BotMsg('<h3>Test h3</h3>abc<p>Test</p>');
        $expect = '=== Test h3 ==='."\n"
            .'abc'."\n\n"
            .'Test';
        $this->assertEquals($expect, $msg->getText());
    }
    function testGetHTML() {
        $msg = new BotMsg('<h1>Test</h1>'."\n"
            .'<p><u><i>This.</i></u></p>'."\n"
            .'<p><b color="#fff">That!</b></p>'."\n"
            .'<p><a>http://jacekk.info</a></p>');
        $expect = '<h1>Test</h1>'."\n"
            .'<p><u><i>This.</i></u></p>'."\n"
            .'<p><b style="color:#fff;">That!</b></p>'."\n"
            .'<p><a href="http://jacekk.info">http://jacekk.info</a></p>';
        $this->assertEquals($expect, $msg->getHTML());
        $this->assertEquals($expect, (string)$msg);
    }
    function testHTMLError() {
        $oldhandler = set_error_handler('errorToException');
        $msg = new BotMsg('<![CDATA[ <p></p> ]]>');
        $msg->getHTML();
        set_error_handler($oldhandler);
    }
    function testSleep() {
        $msg = new BotMsg('<h1>Test</h1><p><u><i>This.</i></u></p><p><b>That!</b></p>');
        $raw = $msg->getRaw();
        $text = $msg->getText();
        $html = $msg->getHTML();
        $serialized = serialize($msg);
        $msg = unserialize($serialized);
        $this->assertEquals($raw, $msg->getRaw());
        $this->assertEquals($text, $msg->getText());
        $this->assertEquals($html, $msg->getHTML());
    }
}
tests/Core/BotSessionTest.php
New file
@@ -0,0 +1,136 @@
<?php
class BotSessionTest extends PHPUnit_Framework_TestCase {
    function testSessionFolder() {
        $dbFolder = dirname(__FILE__).'/../../database';
        $this->assertTrue(is_writable($dbFolder));
        $this->assertTrue(count(glob($dbFolder.'/*.sqlite')) == 0);
    }
    /**
     * @depends testSessionFolder
     */
    function testPullEmpty() {
        $dbFolder = dirname(__FILE__).'/../../database';
        $session = new BotSession('test://user1@test');
        $session->setClass('test');
        $this->assertEquals(array(), $session->pull());
        $this->assertTrue(count(glob($dbFolder.'/*.sqlite')) == 1);
    }
    /**
     * @depends testPullEmpty
     * @expectedException Exception
     */
    function testSetClass() {
        $session = new BotSession('test://user1');
        $session->pull();
    }
    /**
     * @depends testPullEmpty
     */
    function testLegacyImport() {
        $dbFolder = dirname(__FILE__).'/../../database';
        $oldDbFolder = $dbFolder = dirname(__FILE__).'/../../db';
        $data = array('test' => true, 'other' => 'yes, sir!');
        $data_serialized = serialize($data);
        $this->assertTrue(mkdir($oldDbFolder));
        $this->assertTrue(is_writable($oldDbFolder));
        $this->assertTrue(mkdir($oldDbFolder.'/test'));
        $filename = $oldDbFolder.'/test/testUser.ggdb';
        $this->assertEquals(strlen($data_serialized), file_put_contents($filename, $data_serialized));
        $this->assertEquals($data_serialized, file_get_contents($filename));
        $session = new BotSession('test://testUser@test');
        $session->setClass('test');
        $this->assertTrue(isset($session->test));
        $this->assertEquals($data, $session->pull());
        $this->assertFalse(file_exists($filename));
        $this->assertTrue(rmdir($oldDbFolder.'/test'));
        $this->assertTrue(rmdir($oldDbFolder));
    }
    /**
     * @depends testPullEmpty
     */
    function testManualExample() {
        $session = new BotSession('test://user1@test');
        $session->setClass('test');
        // Ustawienie pojedynczej wartości
        $session->zmienna = 'To jest test';
        $this->assertTrue(isset($session->zmienna));
        $this->assertEquals('To jest test', $session->zmienna);
        // Usunięcie pojedynczej wartości
        unset($session->zmienna);
        $this->assertFalse(isset($session->zmienna));
        $this->assertEquals(NULL, $session->zmienna);
        // Ustawienie pojedynczej wartości ponownie
        $session->zmienna = 'To jest test';
        $this->assertTrue(isset($session->zmienna));
        $this->assertEquals('To jest test', $session->zmienna);
        // Usunięcie wszystkich danych
        $session->truncate();
        $this->assertFalse(isset($session->zmienna));
        $this->assertEquals(NULL, $session->zmienna);
        $this->assertEquals(array(), $session->pull());
        // Dopisanie (nadpisanie) danych
        $array = array(
            'zmienna' => 'To jest test2',
            'zmienna2' => new DateTime('2012-01-10')
        );
        $session->push($array);
        $this->assertEquals('To jest test2', $session->zmienna);
        $this->assertEquals($array, $session->pull());
        // push() nie usuwa istniejących danych
        $session->zmienna3 = '333';
        $session->push($array);
        $this->assertNotEquals($array, $session->pull());
        unset($this->session);
    }
    /**
     * @depends testManualExample
     */
    function testManualExample2() {
        $session = new BotSession('test://user1@test');
        $session->setClass('test');
        $array = array(
            'zmienna' => 'To jest test2',
            'zmienna2' => new DateTime('2012-01-10'),
            'zmienna3' => '333'
        );
        $this->assertEquals($array, $session->pull());
        $session->setClass('test2');
        $this->assertEquals(array(), $session->pull());
    }
    /**
     * @depends testManualExample2
     */
    function testCleanup() {
        $dbFolder = dirname(__FILE__).'/../../database';
        foreach(glob($dbFolder.'/*.sqlite') as $file) {
            unlink($file);
        }
    }
}
tests/Core/Legacy/JSArrayTest.php
New file
@@ -0,0 +1,29 @@
<?php
class JSArrayTest extends PHPUnit_Framework_TestCase {
    public function testEmptyString() {
        $result = jsarray::parse('');
        $this->assertSame(NULL, $result);
    }
    public function testEmptyArray() {
        $result = jsarray::parse('[]');
        $this->assertEquals(array(), $result);
    }
    public function testNestedArrays() {
        $array = array(
            array(1, 2, array(), 5, array(6, array(7, 8))),
            array(9),
            '10'
        );
        $array_js = json_encode($array);
        $array_decoded = jsarray::parse($array_js);
        $this->assertEquals($array, $array_decoded);
    }
    public function testInvalid() {
        $result = jsarray::parse('()');
        $this->assertSame(FALSE, $result);
    }
}
tests/autoload.php
New file
@@ -0,0 +1,2 @@
<?php
require_once(dirname(__FILE__).'/../class/std.php');