yakoffka.ru
    грабли, костыли и велосипеды php, css, html, js и прочего

    PHP 2016. Уровень 3.2: ООП + SQLite

    ошибки phpSQLite — компактная встраиваемая СУБД. конспект-памятка видеокурса 'Программирование на PHP 2016'. Лектор Борисов Игорь Олегович. Учебный Центр «Специалист» при МГТУ им. Н.Э.Баумана.

    конспектировал Капустин Яков

    оглавление

    SQLite (/ˌɛskjuːɛlˈlaɪt/ или /ˈsiːkwəl.laɪt/) — компактная встраиваемая СУБД. Исходный код библиотеки передан в общественное достояние. В 2005 году проект получил награду Google-O’Reilly Open Source Awards.

    PHP поддерживает и процедурный и объектно-ориентированный подход при работе с SQLite. Кроме того, в SQLite существует много встроенных решений, выполненных в объектно-ориентированном стиле, и, что еще важнее, в последнее время наблюдается тенденция к отказу от процедурной парадигмы в пользу объектно-ориентированной.

    PHP Code:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

      
    // создаём или открываем базу данных
      
    if($db = new SQLite3("/full/path/to/_php32/test.db")){
        echo 
    __LINE__." YES<br>\n";
      }else{echo 
    __LINE__." NO<br>\n";}

      
    // закрываем базу данных без удаления объекта
      
    if($db->close()){
        echo 
    __LINE__." YES<br>\n";
      }else{echo 
    __LINE__." NO<br>\n";}

      
    // открываем другую базу данных
      
    if($db->open("/full/path/to/_php32/another.db")){
        echo 
    __LINE__." YES<br>\n";
      }else{echo 
    __LINE__." NO<br>\n";}

      
    // удаляем объект
      
    unset($db);
      
    Result:
    4 YES
    9 YES
    15 NO

    Создаём в директории _php32/_classes файлы с описанием интерфейса и класса, а также save_news.inc.php, get_news.inc.php и delete_news.inc.php.

    Все строковые данные прогоняем через функцию escapeString().

    Структура приложения 'Лента новостей':

    • laboratory_3.2.1.php - основной файл новостной ленты;
    • INewsDB.class.php - интерфейс INewsDB с декларациями методов для новостной ленты;
    • NewsDB.class.php - класс NewsDB реализующий интерфейс INewsDB;
    • save_news.php - php-код обработки данных для добавления записи в таблицу БД;
    • delete_news.php - php-код обработки данных для удаления записи из таблицы БД;
    • get_news.php - вывод списка записей из таблицы БД.
    Содержимое файла '_php32/_classes/INewsDB.class.php':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <?php
      
    /*
      *  Interface INewsDB
      *  содержит основные методы работы с новостной лентой
      */
      
    Interface INewsDB{
        
    /*
        *  Добавление новой записи в новостную ленту
        *
        *  @param string $title - заголовок новости
        *  @param string $category - категория новости
        *  @param string $description - текст новости
        *  @param string $source - источник новости
        *
        *  @return boolean - результат успех/ошибка
        */
        
    function saveNews($title,$category,$description,$source);

        
    /*
        *  Выборка всех записей в новостную ленту
        *
        *  @return array - результат в виде массива
        */
        
    function getNews();

        
    /*
        *  Удаление записи из новостной ленты
        *
        *  @param integer $id - id новости
        *
        *  @return boolean - результат успех/ошибка
        */
        
    function deleteNews($id);

      }
      
    Содержимое файла '_php32/_classes/NewsDB.class.php':
    001
    002
    003
    004
    005
    006
    007
    008
    009
    010
    011
    012
    013
    014
    015
    016
    017
    018
    019
    020
    021
    022
    023
    024
    025
    026
    027
    028
    029
    030
    031
    032
    033
    034
    035
    036
    037
    038
    039
    040
    041
    042
    043
    044
    045
    046
    047
    048
    049
    050
    051
    052
    053
    054
    055
    056
    057
    058
    059
    060
    061
    062
    063
    064
    065
    066
    067
    068
    069
    070
    071
    072
    073
    074
    075
    076
    077
    078
    079
    080
    081
    082
    083
    084
    085
    086
    087
    088
    089
    090
    091
    092
    093
    094
    095
    096
    097
    098
    099
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    <?php
      
    class NewsDB implements INewsDB{
        const 
    DB_NAME "/full/path/to/_php32/news.db";
        private 
    $_db;
        public 
    $title;
        public 
    $category;
        public 
    $description;
        public 
    $source;

        function 
    __get($n){  // геттер для получения значения приватного свойства _db
          
    if($n=="_db"){
            return 
    $this->_db;
            throw new 
    Exception("unknown property!");
          }
        }

        function 
    __construct(){
          
    $this->_db = new SQLite3(self::DB_NAME);
          if(
    filesize(self::DB_NAME)==0){
            try{
              
    $sql="CREATE TABLE msgs(
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                title TEXT,
                category INTEGER,
                description TEXT,
                source TEXT,
                datetime INTEGER
              )"
    ;
              if(!
    $this->_db->exec($sql)){throw new Exception($this->_db->lastErrorMsg());}

              
    $sql="CREATE TABLE category(
                id INTEGER,
                name TEXT
              )"
    ;
              if(!
    $this->_db->exec($sql)){throw new Exception($this->_db->lastErrorMsg());}

              
    $sql="INSERT INTO category(id,name)
                SELECT 1 as id, 'Наука' as name
                UNION SELECT 2 as id, 'Образование' as name
                UNION SELECT 3 as id, 'Культура' as name
              "
    ;
              if(!
    $this->_db->exec($sql)){throw new Exception($this->_db->lastErrorMsg());}

            }catch(
    Exception $e){
              
    // здесь можно выполнить запись в лог, отправку email и прочее
              
    echo "exception: ".$e->getMessage()."in file: ".$e->getFile()."in line: ".$e->getLine()."<br>\n";
            }
          }else{
          }
        }

        function 
    __destruct(){
          unset(
    $this->_db);
        }

        function 
    saveNews($title,$category,$description,$source){
          
    $datetime=time();
          
    $sql="INSERT INTO msgs(
            title,
            category,
            description,
            source,
            datetime)
          VALUES(
            '
    $title',
            
    $category,
            '
    $description',
            '
    $source',
            
    $datetime)";

          return 
    $this->_db->exec($sql);

        }

        function 
    getNews(){
          
    $sql="SELECT msgs.id as id,
              title,
              category.name as category,
              description,
              source,
              datetime
            FROM msgs, category
            WHERE category.id = msgs.category
            ORDER BY msgs.id DESC"
    ;
          
    $res $this->_db->query($sql);
          if(
    $res){
            return 
    $this->db2arr($res);
          }else{
            return 
    false;
          }
        }

        function 
    db2arr($data){
          
    $arr=[];
          while(
    $row=$data->fetchArray(SQLITE3_ASSOC)){$arr[]=$row;}
          return 
    $arr;
        }

        function 
    deleteNews($id){
          try{
            
    $sql="DELETE FROM msgs WHERE id=$id";
            
    $res=$this->_db->exec($sql);
            if(!
    $res){
              throw new 
    Exception($this->_db->lastErrorMsg());
            }else{
              return 
    TRUE;
            }
          }catch(
    Exception $e){
            
    // $e->getMessage(); - при необходимости посылаем себе письмо или чего еще
            
    return FALSE;// и возвращаем FALSE
          
    }
        }

        function 
    clearStr($str){
          
    $str=strip_tags($str);
          return 
    $this->_db->escapeString($str);
        }

        function 
    clearInt($int){
          return 
    abs((int)$int);
        }

        public function 
    printProp(){
          echo 
    "Prop: <br>\n";
          foreach(
    $this as $name=>$val){// перебор свойств объекта
            
    echo $name$val;<br>";
          }unset(
    $name,$val);
        }
      }
      
    Содержимое файла '_php32/_classes/save_news.inc.php':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <?php
      $t
    =$news->clearStr($_POST["title"]);
      
    $c=$news->clearStr($_POST["category"]);
      
    $d=$news->clearStr($_POST["description"]);
      
    $s=$news->clearStr($_POST["source"]);

      if(empty(
    $t) or empty($d)){
        
    $errMsg.="Заполните все поля!<br>\n";
      }else{
        if(!
    $news->saveNews($t,$c,$d,$s)){
          
    $errMsg.="Произошла ошибка при добавлении новости.<br>\n";
        }else{
          
    header("Location: https://yakoffka.ru/conspects/laboratory_3.2.1");// избавляемся от $_POST
          
    exit;
        }
      }

      
    Содержимое файла '_php32/_classes/get_news.inc.php':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <?php
      $result
    =$news->getNews();
      if(!
    is_array($result)){
        
    $errMsg='Ошибка вывода ленты новостей.<br>\n';
        
    $result->columnType(0);
        
    $result->columnType(1);
        
    $result->columnType(2);
        
    $result->columnType(3);

      }else{
        echo 
    '<p>новостей: '.count($result).'шт.</p>';

        foreach(
    $result as $key=>$arr_news){
          echo 
    "
          <div class='news'>
            <h2>"
    .$arr_news['title']."</h2>
            <!--div class='id'>id="
    .$arr_news['id']."</div-->
            <div class='datetime'>"
    .date('Y.m.d',$arr_news['datetime'])." ".$arr_news['category']."</div>
            <div class='description'>"
    .$arr_news['description']."</div>
            <div class='source'>источник: "
    .$arr_news['source']."</div>
            <form action='https://yakoffka.ru/conspects/laboratory_3.2.1' method='post'>
              <input type='hidden' name='del_news' value='"
    .$arr_news['id']."'>
              <input type='submit' value='удалить эту новость'></p>
            </form>
          </div>"
    ;
        }
      }
      
    Содержимое файла '_php32/_classes/delete_news.inc.php':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    <?php
      $id
    =$news->clearInt($_POST['del_news']);
      if(
    $id>0){
        if(!
    $news->deleteNews($id)){
          
    $errMsg="Ошибка удаления записи.<br>\n";
        }else{
          
    header("Location: https://yakoffka.ru/conspects/laboratory_3.2.1");// избавляемся от $_POST
          //exit;
          
    $errMsg="Ошибка удаления записи.<br>\n";
        }
      }
      

    Создаём основной файл новостной ленты. Также добавим правило RewriteRule в .htaccess для вновь создаваемого файла.

    Содержимое файла 'laboratory_3.2.1.php' '/full/path/to/laboratory_3.2.1.php':
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41

      
    // реализуем метод автозагрузки
      
    spl_autoload_register(function($class){
        include(
    DIR_SRC_CONSP_PHP32."/_classes/$class.class.php");
      });

      
    $errMsg="";

      
    $news=new NewsDB();

      if(
    $_SERVER['REQUEST_METHOD']=='POST'){
        if(
    $_POST['title']){
          require 
    DIR_SRC_CONSP_PHP32."/_classes/save_news.inc.php";
        }elseif(
    $_POST['del_news']){
          require 
    DIR_SRC_CONSP_PHP32."/_classes/delete_news.inc.php";
        }
      }
      
        
    $mc.="
          <p class='err_mess'><?php echo \$errMsg;?></p>

          <?php
            echo \"
              <h2>Добавление новости</h2>
              <form action='https://yakoffka.ru/conspects/laboratory_3.2.1' method='post'>
                <p>Заголовок новости: <input type='text' name='title' style='width:200px;'>
                Выберите категорию:
                <select name='category'>
                  <option value='1'>Наука</option>
                  <option value='2'>Образование</option>
                  <option value='3'>Культура</option>
                </select>
                Текст новости:</p>
                <textarea name='description' cols='90' rows='10' style='width:100%'></textarea><br>
                <p>Источник: <input type='text' name='source'> <input type='submit' value='Добавить'></p>
              </form>
            \";
            require('/full/path/to/_php32/_classes/get_news.inc.php');
          ?>
        "
    ;