За последние 24 часа нас посетили 17354 программиста и 1600 роботов. Сейчас ищет 891 программист ...

Небольшой класс для работы с сериализированными данными

Тема в разделе "Решения, алгоритмы", создана пользователем pixaye, 24 апр 2014.

  1. pixaye

    pixaye Активный пользователь

    С нами с:
    30 окт 2013
    Сообщения:
    67
    Симпатии:
    0
    Итак, на хабре я прочитал что самый быстрый способ чтения конфигов — это чтение сериализованных данных. Работает быстрее остальных, не тормозит на больших объемах данных. Правда, при этом конфигурационные файлы править вручную достаточно сложно. (копипаста с хабры)

    Так вот, для себя написал небольшой класс для работы с такими данными, потому что в ручную мягко говоря слишком сложно редактировать такие данные (по крайней мере для меня)

    Вот сам класс

    Код (PHP):
    1. <?php
    2.  
    3. /**
    4.  * The MIT License (MIT)
    5.  *
    6.  * Copyright (c) 2014 Pixaye
    7.  *
    8.  * Permission is hereby granted, free of charge, to any person obtaining a copy
    9.  * of this software and associated documentation files (the "Software"), to deal
    10.  * in the Software without restriction, including without limitation the rights
    11.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    12.  * copies of the Software, and to permit persons to whom the Software is
    13.  * furnished to do so, subject to the following conditions:
    14.  *
    15.  * The above copyright notice and this permission notice shall be included in all
    16.  * copies or substantial portions of the Software.
    17.  *
    18.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    19.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    20.  * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
    21.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    22.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    23.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    24.  * SOFTWARE.
    25.  */
    26.  
    27. /**
    28.  * Managing logs, if LOG_ENABLED = true, that logs are maintained,
    29.  * If it's equals false, then respectively, while performing the action, it will not log the.
    30.  */
    31.  
    32. define('LOG_ENABLED', false);
    33.  
    34. class Gee
    35. {
    36.     /**
    37.      * @var array
    38.      */
    39.     protected $_gee_file;
    40.  
    41.     /**
    42.      * @param $file
    43.      * @return bool|mixed
    44.      */
    45.     public function _gee_load($file)
    46.     {
    47.         if (file_exists($file)) {
    48.             //Return associative array
    49.             return unserialize(file_get_contents($file));
    50.         } else {
    51.             $this->_gee_file = $file;
    52.             $this->_gee_file_error();
    53.             return false;
    54.         }
    55.     }
    56.  
    57.     /**
    58.      * @param $file
    59.      * @param array $arr
    60.      * @return bool
    61.      */
    62.     public function _gee_save($file, $arr = array())
    63.     {
    64.         if (file_exists($file)) {
    65.             $nc = file_get_contents($file);
    66.             $sc = serialize($arr);
    67.             if ($nc == $sc) {
    68.                 $this->_gee_log('File did not edited ' . $file . ' ,writing new file aborted!');
    69.                 return false;
    70.             } else {
    71.                 $this->_gee_file = $file;
    72.                 file_put_contents($file, serialize($arr));
    73.                 $this->_gee_log('File ' . $file . ' successfully updated!');
    74.                 return true;
    75.             }
    76.         } else {
    77.             $this->_gee_log('File ' . $file . ' not found!');
    78.             $this->_gee_file = $file
    79.             $this->_gee_file_error();
    80.             return false;
    81.         }
    82.     }
    83.  
    84.     /**
    85.      * @return bool
    86.      */
    87.     protected function _gee_file_error()
    88.     {
    89.         exit('File <b>'. $this->_gee_file. '</b> does not exist!');
    90.     }
    91.  
    92.     /**
    93.      * @param null $msg
    94.      * @return bool
    95.      */
    96.     protected function _gee_log($msg = null)
    97.     {
    98.         if (LOG_ENABLED == true) {
    99.             if (file_exists('logs/log.txt')) {
    100.                 if (filesize('logs/log.txt') > 10000000) {
    101.                     file_put_contents("logs/log.txt", "");
    102.                 }
    103.                 if (!empty($msg)) {
    104.                     file_put_contents('logs/log.txt', date("F j, Y, g:i a, H:i:s") . ' :: ' . $msg . "\r\n", FILE_APPEND);
    105.                     return true;
    106.                 } else {
    107.                     return false;
    108.                 }
    109.             } else {
    110.                 echo "Can't write in log, file does not exist!";
    111.                 return false;
    112.             }
    113.         } else {
    114.             return false;
    115.         }
    116.     }
    117. }
    118.  
    Класс так же может вести логи, хотя я не уверен что они нужны но всё же, если поставить константе LOG_ENABLED значение true, то все действия будут логгироваться. Но для начала нужно создать папку logs, а в ней log.txt

    Парсинг файлов происходит следующим образом, вы просто должны вызвать метод _gee_load('путь к файлу');
    Метод возвращает ассоциативный массив и работает быстро.

    Сохранение данных в файл производится методом _gee_save('путь к файлу', массив типа ключ=>значение);
    P.S. если файл существует и он не был изменен, а метод вызывается, то данные в файл не будут сохранены (типа я кеш изобрел, хотя куда мне). Расширение файлов: *.gee

    Делал для себя, т.к. мне нужна высокая скорость работы моего фреймворка.

    Так же очень важно поставить в папке с файлами .htaccess с
    Код (Text):
    1. deny from all
    или же если файл не в папке а скажем в корне сайта, то
    Код (Text):
    1. <Files "*.gee">
    2. deny from all
    3. </Files>
    Go!
     
  2. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    т.е. вручную менять конфиг нельзя, так как хранится он в сериализованном виде, тока через класс? o_O
    чем json неустроил? или yaml какой нить. наглядно и быстро пашет
     
  3. pixaye

    pixaye Активный пользователь

    С нами с:
    30 окт 2013
    Сообщения:
    67
    Симпатии:
    0
    Ну работа с сериализированными данными быстрее чем с json и yaml
    Кто сказал что нельзя? Можно но придется немного помучатся :)
     
  4. d1gi

    d1gi Активный пользователь

    С нами с:
    24 май 2009
    Сообщения:
    326
    Симпатии:
    0
    первое чтение да медленне на ямле, а потом результат ямла дампится в кеш в виде сериализованного массива и всё... единственное, нужно для дебаг режима настроить, чтобы перед чтением конфига из кеша, сверять дату модификации оригинального ямла... либо просто ручками очищать кеш :) на продакшине разумеется прямое чтение из кеша, притом кеш можно хранить даже не в файлах, а в томже APCu, Memcached, WinCache и т.д...
     
  5. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    а зачем придумывать решение - которое заставляет мучаться?
    конфиг должен быть простой и наглядный. чтоб менять его можно было хоть руками, хоть программой.
    вариант с сериализацией - тупиковый. за мнимую производительность приходится платить полной потерей самой сути конфигурационного файла.

    ну подумайте. что есть конфиг?
    это место управления Человеком - Машины. т.е. конфиг пишется и редактируется - Человеком. он должен быть понятен Человеку, и только потом Машине.

    дарю идею - хранить конфиг в виде base64. это ваще будет модно)
     
  6. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.770
    Адрес:
    :сердА
    А читал очень невнимательно и поверхностно. Читал бы менее поверхностно и более вдумчиво, да еще бы и с комментами, обратил бы внимание на то, что:

    1) Во главу угла надо ставить удобство.
    2) Чтобы добиться более-менее заметных при получении замеров расхождений, которые нельзя было бы списать на погрешность/просаживание процессора по левой причине, там использовались "конфиги" с сотнями и тысячами строк-параметров.
    3) Конфиг парсится один раз. Хочется мегапроизводительности - составь конфиг из инструкций, пишущих его в шаредмемори. Но конфиг - это не тонкое место. Это разовая операция. Она всегда 100% предсказуема в плане потребления ресурсов. Никогда не просядет. Не породит утечки. Не имеет алгоритма, который можно отрефакторить. Это не то место, где стоит экономить микросекунды и килобайтики.

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

    Удобство конфига для человека - это не просто блажь. Тривиальная ситуация - выгрузил проект с локалки на внешку, и все перестало работать, потому что:
    1) хоумдиректори левое.
    2) данные для бд левые.
    3) основной домен левый.
    4) прочие левые данные, специфичные для сервера.

    И все хорошо, но проект не работает, или работает неадекватно. И конфигуратор тоже может лежать кверху пузом.
    Разруливается это все предельно просто - поправил конфиг, перезалил, обновил страничку, воскричал "алилуйя!". Конфигураторы нужны, но работающие с удобными человеку конфигами.

    З.Ы. За старания - пять. Может для кого-то и будет полезным, кто знает.
     
  7. pixaye

    pixaye Активный пользователь

    С нами с:
    30 окт 2013
    Сообщения:
    67
    Симпатии:
    0
    Спасибо, ну в принципе разница в миллисекундах действительно никакой роли не играет.
     
  8. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    если хранить конфиг в пхп-файле, то он будет и читаем, и редактируем, и кешируем опкешереами. и будет ацки быстро и ацки удобно.

    а для кеша данных, которые человеком не читаются, лучше конечно serialize, ибо он неимоверно быстрее всего остального.
     
  9. pixaye

    pixaye Активный пользователь

    С нами с:
    30 окт 2013
    Сообщения:
    67
    Симпатии:
    0
    Артём, Yaml работает намного медленнее чем тот же Ini который я использовал у себя.
    Среднее время выполнения скрипта с Ini - 0.0150s
    Тот же скрипт с Yaml (Symfony) - 0.0290s
     
  10. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.770
    Адрес:
    :сердА
    Таки вот же жь. Я, к примеру, оч люблю генерируемые кеши и конфиги хранить именно в виде пых-файлов по тем же причинам. Вот там, если есть опкэшер, точно производительность и расход памяти значительно сокращаются. И не нужно ничего парсить. Не нужно создавать никакие структуры данных. Они уже созданы и подключаются прямо в пых в готовом виде - это же прекрасно!

    Кстати, в комментах к той статье на хабре тоже было подобное указано, как важное замечание в пользу "монструозных дефайнов" и конфигов в виде статик-класса для модных и массива для любителей боли, неприемлющих помощь со стороны IDE при поиске нужного параметра.
     
  11. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    сериалайз работает очень быстро, а если "генерируемые" кеши не читаются людьми - то самое то именно сериалайз юзать.
     
  12. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    при чем здесь кеши. речь шла о конфиге. сериалайз для конфига неприемлем.
    конфиг читается один раз - а потом тупо висит в памяти. кеш тут какбе и ненужен.
     
  13. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    просто подход очень для кеша подходящий =)
    а про конфиг вроде обмусолили уже.
     
  14. pixaye

    pixaye Активный пользователь

    С нами с:
    30 окт 2013
    Сообщения:
    67
    Симпатии:
    0
    Вот и нашли применение для класса :)
     
  15. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.770
    Адрес:
    :сердА
    Я генерю удобочитаемо. Редко, но приходится править. В основном при отладке.
     
  16. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    ну про отладочное речь не идёт. там что угодно можно делать.

    вот мои кешеры. на файлах быстрее, чем через мемкеш, но надо самому следить за переполнением.

    мемкеш:
    Код (PHP):
    1. <?php
    2.  
    3. class Cache
    4. {
    5.  
    6.   private static $mc = null;
    7.  
    8.   private static function Init() {
    9.     self::$mc = new Memcache();
    10.     foreach (CFG::$pincms['cache']['servers'] as $server) {
    11.       self::$mc->addServer($server['host'], $server['port']);
    12.     }
    13.     return true;
    14.   }
    15.  
    16.   public static function Get($hash) {
    17.     if (is_null(self::$mc)) {
    18.       if (self::Init() == false) {
    19.         return false;
    20.       }
    21.     }
    22.     // debug mode
    23.     //    $r = self::$mc->get($hash);
    24.     //    return $r;
    25.     return self::$mc->get($hash);
    26.   }
    27.  
    28.   public static function Set($hash, $content) {
    29.     if (is_null(self::$mc)) {
    30.       if (self::Init() == false) {
    31.         return false;
    32.       }
    33.     }
    34.     return self::$mc->set($hash, $content);
    35.   }
    36.  
    37. } 
    на файликах:
    Код (PHP):
    1. <?php
    2.  
    3. class Cache {
    4.   private static $path = '';
    5.   private static $ok = false;
    6.  
    7.   public static function Init() {
    8.     if (self::$ok === true) {
    9.       return true;
    10.     }
    11.     PinCMS::$binhash = false;
    12.     self::$path = ROOT . DS . 'filecache' . DS;
    13.     if (!is_dir(self::$path)) {
    14.       mkdir(self::$path);
    15.     }
    16.     if (touch(self::$path . 'test')) {
    17.       self::$ok = true;
    18.     }
    19.     self::$ok = true;
    20.     return self::$ok;
    21.   }
    22.  
    23.   public static function Get($hash) {
    24.     if (!self::Init()) {
    25.       return false;
    26.     }
    27.     $fp = self::$path . $hash;
    28.     if (!file_exists($fp)) {
    29.       return false;
    30.     }
    31.     $content = file_get_contents(self::$path . $hash);
    32.     if ($content === false) {
    33.       return false;
    34.     }
    35.     return unserialize($content);
    36.   }
    37.  
    38.   public static function Set($hash, $content) {
    39.     if (!self::Init()) {
    40.       return false;
    41.     }
    42.     $fp = self::$path . $hash;
    43.     if (!touch($fp)) {
    44.       return false;
    45.     }
    46.     $content = serialize($content);
    47.     return file_put_contents(self::$path . $hash, $content);
    48.   }
    49.  
    50. }
    51.  
    52. Cache::Init(); 
     
  17. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    -я бы добавил возможность отключать кеширование.
    -еще добавил бы метод для удаления кеша по ключу.
    -еще бывает удобно если можно указать время жизни для элемента кеша. чтобы все само обновлялось когда таймаут просрочится.
    -еще путь сохранения для файлов надо читать из конфига, а не прописывать жестко внутри класса.
    -странный код:
    Код (PHP):
    1. if (touch(self::$path . 'test')) {
    2.  self::$ok = true;
    3. }
    4. self::$ok = true;
    -еще несмогу иметь сразу два кешера с разными свойствами, ибо статики не к месту связывают по рукам и ногам

    это так на вскидку.
     
  18. d1gi

    d1gi Активный пользователь

    С нами с:
    24 май 2009
    Сообщения:
    326
    Симпатии:
    0
    так результат ямл парсера скинь в кеш :) и будут такие же циферки как на "голом пхп" :) зато ямл использовать человеком удобней всего ,а у ini есть ограничения по вложенности.
     
  19. pixaye

    pixaye Активный пользователь

    С нами с:
    30 окт 2013
    Сообщения:
    67
    Симпатии:
    0
    Хм, вынужден не согласиться
    какая разница, написать:
    Код (Text):
    1. host = localhost
    или
    Код (Text):
    1. host: localhost
    Добавлено спустя 3 минуты 6 секунд:
    На Linux-е результаты получше, среднее время выполнения скрипта: 0.0022s (без APC) ,думаю с IIS будет тоже самое.
     
  20. d1gi

    d1gi Активный пользователь

    С нами с:
    24 май 2009
    Сообщения:
    326
    Симпатии:
    0
    в ini всего 1 уровень вложенности
    Код (Text):
    1.  
    2. [db]
    3. name: mybase
    в ямле можно хоть какую вложенность оформлять, а также записывать ассоциативные массивы в одну строчку, например:
    Код (Text):
    1.  
    2. db:
    3.   name: mybase
    4.   cache: { result: apc, query: apc }
    да и много еще чего можно в ямле :) а с кешированием на "скорость" это удобство вообще никак не влияет.
     
  21. pixaye

    pixaye Активный пользователь

    С нами с:
    30 окт 2013
    Сообщения:
    67
    Симпатии:
    0
    И где это может понадобиться? Я лично не вижу пока что причин применять Yaml
     
  22. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.156
    Симпатии:
    1.770
    Адрес:
    :сердА
    У меня подключаемые компоненты используют метафайлы с уровнем вложенности больше 1. Например секция настроек, состоящая из подсекций, состоящих из подсекций, в зависимости от типа параметра. Но я для себя выбрал не YAML, а XML. Личное предпочтение.

    А вот для конфига системы в целом, вложенность имеет сомнительную ценность.
     
  23. d1gi

    d1gi Активный пользователь

    С нами с:
    24 май 2009
    Сообщения:
    326
    Симпатии:
    0
  24. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    она реализована до них, поэтому я не делал ничего такого.

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

    реализовано до кешера, опять же.

    может быть. в текущих условиях не требуется.

    походу дебаговая строка, которую я тупо забыл убрать. благодарствую.

    дык и не надо.