| | |
| | | * w szczególności jego ustawienia. |
| | | */ |
| | | class BotSession { |
| | | private $PDO; |
| | | /** |
| | | * Instancja PDO tworzona w metodzie {@link BotSession::init()}. |
| | | * @var PDO $PDO |
| | | */ |
| | | protected $PDO; |
| | | |
| | | /** |
| | | * Nazwa modułu, którego zmienne klasa przetwarza |
| | | * @var string $class max. 40 znaków |
| | | * Katalog, w którym trzymane są dane sesyjne użytkowników. |
| | | * @var string $sessionDir |
| | | */ |
| | | protected $sessionDir; |
| | | |
| | | /** |
| | | * Katalog, w którym trzymane są dane sesyjne użytkowników |
| | | * z poprzedniej wersji bota. |
| | | * @var string $legacySessionDir |
| | | */ |
| | | protected $legacySessionDir; |
| | | |
| | | /** |
| | | * Nazwa modułu (max. 40 znaków), którego zmienne klasa aktualnie przetwarza, |
| | | * ustawiana metodą {@link BotSession::setClass()}. |
| | | * @var string $class |
| | | */ |
| | | protected $class = ''; |
| | | protected $class_empty = TRUE; |
| | | |
| | | /** |
| | | * Pseudo-URL użytkownika. |
| | | * @see BotUser |
| | | * @var string $user URL użytkownika |
| | | * @var string $user |
| | | */ |
| | | private $user; |
| | | /** |
| | | * Klasa z identyfikatorem użytkownika |
| | | * @var BotUser $user_struct |
| | | */ |
| | | private $user_struct; |
| | | protected $user; |
| | | |
| | | /** |
| | | * Inicjuje klasę w zależności od użytkownika |
| | | * Inicjuje klasę dla podanego użytkownika |
| | | * @param string $user Pseudo-URL użytkownika |
| | | * @param string $sessionDir Katalog z danymi, domyślnie BOT_TOPDIR/database |
| | | * @param string $legacySessionDir Katalog z danymi ze starej wersji bota, domyślnie BOT_TOPDIR/db |
| | | */ |
| | | function __construct($user) { |
| | | $this->user = sha1($user); |
| | | $this->user_struct = parse_url($user); |
| | | |
| | | $this->class_empty = FALSE; |
| | | public function __construct($user, $sessionDir = NULL, $legacySessionDir = NULL) { |
| | | if(empty($sessionDir)) { |
| | | $sessionDir = BOT_TOPDIR.'/database'; |
| | | } |
| | | if(empty($legacySessionDir)) { |
| | | $legacySessionDir = BOT_TOPDIR.'/db'; |
| | | } |
| | | |
| | | private function init() { |
| | | if(strlen($this->class) == 0 && !$this->class_empty) { |
| | | throw new Exception('Przed użyciem $msg->session należy ustawić nazwę modułu za pomocą metody setClass - patrz "Poradnik tworzenia modułów", dział "Klasa BotMessage", rozdział "Pole $session".'); |
| | | $this->user = $user; |
| | | $this->sessionDir = $sessionDir; |
| | | $this->legacySessionDir = $legacySessionDir; |
| | | } |
| | | |
| | | /** |
| | | * Sprawdza ustawienie pola {@link BotSession::$class} oraz, jeśli nie została wykonana wcześniej, |
| | | * dokonuje inicjalizacji klasy. |
| | | * Metoda ta winna być wywoływana przez każdą publiczną funkcję operującą na danych. |
| | | * @throws Exception Wyjątek rzucany, gdy przed użyciem metody, nazwa klasy |
| | | * nie została ustawiona metodą {@link BotSession::setClass()} |
| | | */ |
| | | protected function init() { |
| | | if(empty($this->class)) { |
| | | throw new Exception('Przed użyciem mechanizmu sesji należy ustawić nazwę modułu za pomocą metody setClass - patrz "Poradnik tworzenia modułów", dział "Klasa BotMessage", rozdział "Pole $session".'); |
| | | } |
| | | |
| | | if($this->PDO) { |
| | | return NULL; |
| | | } |
| | | |
| | | if(is_file(BOT_TOPDIR.'/database/'.sha1($this->user).'.sqlite')) { |
| | | $this->PDO = new PDO('sqlite:'.BOT_TOPDIR.'/database/'.sha1($this->user).'.sqlite'); |
| | | $this->PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
| | | $this->PDO->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_TO_STRING); |
| | | |
| | | $st = $this->PDO->query('SELECT value FROM data WHERE class=\'\' AND name=\'_version\''); |
| | | $row = $st->fetch(PDO::FETCH_ASSOC); |
| | | if(is_array($row)) { |
| | | $version = (int)$row['value']; |
| | | } |
| | | else |
| | | { |
| | | $version = 0; |
| | | } |
| | | $st->closeCursor(); |
| | | |
| | | if($version < 1) { |
| | | $this->PDO->query('UPDATE data SET class=\'kino\' WHERE class=\'\' AND name=\'kino\''); |
| | | $this->PDO->query('INSERT OR REPLACE INTO data (class, name, value) VALUES (\'\', \'_version\', 1)'); |
| | | $version = 1; |
| | | } |
| | | |
| | | 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\', 4)'); |
| | | $version = 4; |
| | | } |
| | | |
| | | // Inicjalizacja została już przeprowadzona - wyjdź. |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | $this->PDO = new PDO('sqlite:'.BOT_TOPDIR.'/database/'.sha1($this->user).'.sqlite'); |
| | | $dbFile = $this->sessionDir.'/'.sha1(sha1($this->user)).'.sqlite'; |
| | | |
| | | $this->PDO = new PDO('sqlite:'.$dbFile); |
| | | $this->PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
| | | $this->PDO->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_TO_STRING); |
| | | |
| | | $st = $this->PDO->query('SELECT COUNT(name) FROM sqlite_master WHERE type=\'table\' AND name=\'data\''); |
| | | $num = $st->fetch(PDO::FETCH_NUM); |
| | | $schemaExists = $num[0] > 0; |
| | | |
| | | if($schemaExists) { |
| | | $this->updateDatabase(); |
| | | } else { |
| | | try { |
| | | $this->createSchema(); |
| | | $this->importLegacyData(); |
| | | } |
| | | catch(Exception $e) { |
| | | // Import danych nie udał się - usuń pozostałości. |
| | | if(file_exists($dbFile)) { |
| | | @unlink($dbFile); |
| | | } |
| | | throw $e; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Ustawia nazwę modułu/klasy, której zmienne będą przetwarzane |
| | | * @param string $class Nazwa modułu |
| | | */ |
| | | public function setClass($class) { |
| | | $this->class = $class; |
| | | } |
| | | |
| | | /** |
| | | * Pobiera zmienną o podanej nazwie (getter). |
| | | * @param string $name Nazwa zmiennej. |
| | | * @return mixed Wartość zmiennej lub NULL, jeśli zmienna nie istnieje. |
| | | */ |
| | | public function __get($name) { |
| | | $this->init(); |
| | | |
| | | $st = $this->PDO->prepare('SELECT value FROM data WHERE class=? AND name=?'); |
| | | $st->execute(array($this->class, $name)); |
| | | $st = $st->fetch(PDO::FETCH_ASSOC); |
| | | |
| | | if(is_array($st)) { |
| | | return unserialize($st['value']); |
| | | } |
| | | |
| | | return NULL; |
| | | } |
| | | |
| | | /** |
| | | * Ustawia zmienną o podanej nazwie. |
| | | * @param string $name Nazwa zmiennej. |
| | | * @param mixed $value Wartość do ustawienia. |
| | | */ |
| | | public function __set($name, $value) { |
| | | $this->init(); |
| | | |
| | | $st = $this->PDO->prepare('INSERT OR REPLACE INTO data (class, name, value) VALUES (?, ?, ?)'); |
| | | $st->execute(array($this->class, $name, serialize($value))); |
| | | } |
| | | |
| | | /** |
| | | * Sprawdza czy podana zmienna została ustawiona. |
| | | * @param string $name Nazwa zmiennej do sprawdzenia. |
| | | * @return bool Czy zmienna istnieje? |
| | | */ |
| | | public function __isset($name) { |
| | | $this->init(); |
| | | |
| | | $st = $this->PDO->prepare('SELECT COUNT(name) FROM data WHERE class=? AND name=?'); |
| | | $st->execute(array($this->class, $name)); |
| | | $st = $st->fetch(PDO::FETCH_NUM); |
| | | |
| | | return ($st[0] > 0); |
| | | } |
| | | |
| | | /** |
| | | * Usuwa zmienną o podanej nazwie. |
| | | * @param string $name Nazwa zmiennej do usunięcia. |
| | | */ |
| | | public function __unset($name) { |
| | | $this->init(); |
| | | |
| | | $st = $this->PDO->prepare('DELETE FROM data WHERE class=? AND name=?'); |
| | | $st->execute(array($this->class, $name)); |
| | | } |
| | | |
| | | /** |
| | | * Dodaje tablicę zmiennych do danych użytkownika. |
| | | * @param array $array Tablica zmiennych do dodania. |
| | | */ |
| | | public function push($array) { |
| | | $this->init(); |
| | | |
| | | $this->PDO->beginTransaction(); |
| | | foreach($array as $name => $value) { |
| | | $this->__set($name, $value); |
| | | } |
| | | $this->PDO->commit(); |
| | | } |
| | | |
| | | /** |
| | | * Zwraca wszystkie ustawione zmienne dla modułu. |
| | | * @return array Lista wszystkich zmiennych. |
| | | */ |
| | | public function pull() { |
| | | $this->init(); |
| | | |
| | | $st = $this->PDO->prepare('SELECT name, value FROM data WHERE class=?'); |
| | | $st->execute(array($this->class)); |
| | | $rows = $st->fetchAll(PDO::FETCH_ASSOC); |
| | | |
| | | $return = array(); |
| | | foreach($rows as $row) { |
| | | $return[$row['name']] = unserialize($row['value']); |
| | | } |
| | | |
| | | return $return; |
| | | } |
| | | |
| | | /** |
| | | * Usuwa wszystkie zmienne sesyjne danego modułu. |
| | | */ |
| | | public function truncate() { |
| | | $this->init(); |
| | | |
| | | $st = $this->PDO->prepare('DELETE FROM data WHERE class=?'); |
| | | $st->execute(array($this->class)); |
| | | } |
| | | |
| | | /** |
| | | * Aktualizuje schemat bazy danych oraz dane, w szczególności poprawia błędy |
| | | * wprowadzone we wcześniejszych wersjach (np. brak ustawionej nazwy klasy). |
| | | */ |
| | | private function updateDatabase() { |
| | | $st = $this->PDO->query('SELECT value FROM data WHERE class=\'\' AND name=\'_version\''); |
| | | $row = $st->fetch(PDO::FETCH_ASSOC); |
| | | |
| | | $version = 0; |
| | | if (is_array($row)) { |
| | | $version = (int)$row['value']; |
| | | } |
| | | |
| | | $st->closeCursor(); |
| | | |
| | | switch($version) { |
| | | case 1: |
| | | $this->PDO->query('UPDATE data SET class=\'kino\' WHERE class=\'\' AND name=\'kino\''); |
| | | $this->PDO->query('INSERT OR REPLACE INTO data (class, name, value) VALUES (\'\', \'_version\', 1)'); |
| | | case 2: |
| | | case 3: |
| | | $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\', 4)'); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Tworzy schemat bazy danych sesyjnych. |
| | | */ |
| | | private function createSchema() { |
| | | $this->PDO->query( |
| | | 'CREATE TABLE data ( |
| | | class VARCHAR(50) NOT NULL DEFAULT \'\', |
| | |
| | | ) |
| | | )' |
| | | ); |
| | | |
| | | $this->PDO->query('INSERT INTO data (class, name, value) VALUES (\'\', \'_version\', 4)'); |
| | | } |
| | | |
| | | $files = glob(BOT_TOPDIR.'/db/*/'.$this->user_struct['user'].'.ggdb'); |
| | | /** |
| | | * Importuje dane użytkowników z poprzedniej wersji bota. |
| | | */ |
| | | private function importLegacyData() { |
| | | $userData = parse_url($this->user); |
| | | $files = glob($this->legacySessionDir.'/*/'.$userData['user'].'.ggdb'); |
| | | if(!$files) { |
| | | return; |
| | | } |
| | |
| | | $st->execute(array($this->class, $name, serialize($value))); |
| | | } |
| | | } |
| | | |
| | | $this->PDO->commit(); |
| | | |
| | | foreach($files as $file) { |
| | | unlink($file); |
| | | } |
| | | } |
| | | catch(Exception $e) { |
| | | if(file_exists(BOT_TOPDIR.'/database/'.sha1($this->user).'.sqlite')) { |
| | | @unlink(BOT_TOPDIR.'/database/'.sha1($this->user).'.sqlite'); |
| | | } |
| | | throw $e; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 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(); |
| | | |
| | | $st = $this->PDO->prepare('SELECT value FROM data WHERE class=? AND name=?'); |
| | | $st->execute(array($this->class, $name)); |
| | | $st = $st->fetch(PDO::FETCH_ASSOC); |
| | | |
| | | if(is_array($st)) { |
| | | return unserialize($st['value']); |
| | | } |
| | | else |
| | | { |
| | | return NULL; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Ustawia zmienną o podanej nazwie |
| | | * @param string $name Nazwa zmiennej |
| | | * @param mixed $value Wartość zmiennej |
| | | */ |
| | | function __set($name, $value) { |
| | | $this->init(); |
| | | |
| | | $st = $this->PDO->prepare('INSERT OR REPLACE INTO data (class, name, value) VALUES (?, ?, ?)'); |
| | | $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(); |
| | | |
| | | $st = $this->PDO->prepare('SELECT COUNT(name) FROM data WHERE class=? AND name=?'); |
| | | $st->execute(array($this->class, $name)); |
| | | $st = $st->fetch(PDO::FETCH_NUM); |
| | | |
| | | return ($st[0]>0); |
| | | } |
| | | |
| | | /** |
| | | * Usuwa zmienną o podanej nazwie |
| | | * @param string $name Nazwa zmiennej |
| | | */ |
| | | function __unset($name) { |
| | | $this->init(); |
| | | |
| | | $st = $this->PDO->prepare('DELETE FROM data WHERE class=? AND name=?'); |
| | | $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) { |
| | | $this->__set($name, $value); |
| | | } |
| | | $this->PDO->commit(); |
| | | } |
| | | |
| | | /** |
| | | * Zwraca wszystkie ustawione zmienne danego modułu |
| | | * @return array Lista wszystkich zmiennych |
| | | */ |
| | | function pull() { |
| | | $this->init(); |
| | | |
| | | $st = $this->PDO->prepare('SELECT name, value FROM data WHERE class=?'); |
| | | $st->execute(array($this->class)); |
| | | $st = $st->fetchAll(PDO::FETCH_ASSOC); |
| | | |
| | | $return = array(); |
| | | foreach($st as $row) { |
| | | $return[$row['name']] = unserialize($row['value']); |
| | | } |
| | | |
| | | return $return; |
| | | } |
| | | |
| | | /** |
| | | * Usuwa wszystkie zmienne sesyjne danego modułu. |
| | | */ |
| | | function truncate() { |
| | | $this->init(); |
| | | |
| | | $st = $this->PDO->prepare('DELETE FROM data WHERE class=?'); |
| | | $st->execute(array($this->class)); |
| | | } |
| | | } |
| | | ?> |