Пользователь
0,0
рейтинг
12 июля 2015 в 01:34

Разработка → Вышла PHP 7 beta 1

PHP*
Собственно представлена первая бета-версия PHP 7, хороший анонс с изменениями был на хабре ранее (по альфе).

В тексте новости на сайте опубликована ссылка на документ UPGRADING, где описываются несовместимости «семерки». Помимо выкинутых, как обычно, DEPRECATED функций и расширений (исчезла масса SAPI модулей), заметные изменения произошли в самом языке. Ниже — о части из них.

По порядку из официального документа:

Изменения в обработке переменных

1. Косвенные ссылки на переменные, свойства и методы теперь разбираются слева направо. Восстановить прежний порядок можно фигурными скобками.
$$foo['bar']['baz']   // разбирается как ($$foo)['bar']['baz']    - ранее как ${$foo['bar']['baz']}
$foo->$bar['baz']   // разбирается как ($foo->$bar)['baz']    - ранее как $foo->{$bar['baz']}
$foo->$bar['baz']() // разбирается как ($foo->$bar)['baz']() - ранее как $foo->{$bar['baz']}()
Foo::$bar['baz']()   // разбирается как (Foo::$bar)['baz']()   - ранее как Foo::{$bar['baz']}()


2. Ключевое слово global принимает только простые переменные. Вместо global $$foo->bar следует писать global ${$foo->bar}

3. Скобки вокруг переменных или вызовов функций больше не влияют на поведение. Например, код, где результат функции передается по ссылке:
      function getArray() { return [1, 2, 3]; }

      $last = array_pop(getArray());
      // Strict Standards: Only variables should be passed by reference
      $last = array_pop((getArray()));
      // Strict Standards: Only variables should be passed by reference


сейчас выбросит ошибку strict standards вне зависимости от скобок (ранее во втором вызове ее не было).

4. Элементы массива или свойства объекта, которые были автоматически созданы во время присвоений по ссылке сейчас будут иметь другой порядок. Код:
      $array = [];
      $array["a"] =& $array["b"];
      $array["b"] = 1;
      var_dump($array);

сейчас сгенерирует массив [«a» => 1, «b» => 1], тогда как ранее был [«b» => 1, «a» => 1].

Изменения в обработке list()

1. list() теперь присваивает переменные в прямом порядке (ранее — в обратном), например:
      list($array[], $array[], $array[]) = [1, 2, 3];
      var_dump($array);

сейчас выдаст $array == [1, 2, 3] вместо [3, 2, 1]. Изменился только порядок присвоения, т.е. нормальное использование list() не затронуто.

2. Присвоения с пустым списком list() стали запрещены, следующие выражения ошибочны:
      list() = $a;
      list(,,) = $a;
      list($x, list(), $y) = $a;


3. list() больше не поддерживает распаковку строк (ранее поддерживалась в некоторых случаях). Код:
      $string = "xy";
      list($x, $y) = $string;

установит $x и $y в значение null (без предупреждений) вместо $x == «x» и $y == «y». Более того, list() теперь гарантированно работает с объектами, реализующими интерфейс ArrayAccess, т.е. вот так заработает:
      list($a, $b) = (object) new ArrayObject([0, 1]);

Ранее в обе переменные был бы занесен null.

Изменения в foreach

1. Итерации в foreach() больше не влияют на внутренний указатель массива, который доступен через семейство функций current()/next()/…
      $array = [0, 1, 2];
      foreach ($array as &$val) {
          var_dump(current($array));
      }

сейчас напечатает int(0) три раза. Ранее — int(1), int(2), bool(false)

2. Во время итерирования массивов по значению, foreach теперь пользуется копией массива, и его изменения внутри цикла не повлияют на поведение цикла:
      $array = [0, 1, 2];
      $ref =& $array; // необходимо, чтобы включить старое поведение
      foreach ($array as $val) {
          var_dump($val);
          unset($array[1]);
      }

Код напечатает все значения (0 1 2), ранее второй элемент выкидывался — (0 2).

3. Когда итерируются массивы по ссылке, изменения в массиве будут влиять на цикл. Предполагается, что PHP лучше будет отрабатывать ряд случаев, например, добавление в конец массива:
      $array = [0];
      foreach ($array as &$val) {
          var_dump($val);
          $array[1] = 1;
      }

проитерирует и добавленный элемент. Вывод будет «int(0) int(1)», ранее было только «int(0)».

4. Итерирование обычных (не Traversable) объектов по значению или по ссылке будет вести себя как итерирование по ссылке для массивов. Ранее — аналогично, за исключением более точного позиционирования из предыдущего пункта.

5. Итерирование Traversable объектов не изменилось.

Изменения в обработке аргументов функций

1. Больше нельзя использовать одинаковые имена для аргументов (будет ошибка компиляции):
      public function foo($a, $b, $unused, $unused) {
          // ...
      }


2. Функции func_get_arg() и func_get_args() теперь вернут текущее значение (а не исходное). Например:
      function foo($x) {
          $x++;
          var_dump(func_get_arg(0));
      }
      foo(1);

выведет «2» вместо «1».

3. Похожим образом трейсы в исключениях не будет выводить оригинальные значения, а уже измененные:
      function foo($x) {
          $x = 42;
          throw new Exception;
      }
      foo("string");

теперь выдаст:
Stack trace:
#0 file.php(4): foo(42)
#1 {main}


Ранее было бы так:
Stack trace:
#0 file.php(4): foo('string')
#1 {main}


Хоть это и не влияет на исполнение, но следует иметь это в виду при отладке. То же ограничение теперь и в debug_backtrace() и прочих функциях, исследующих аргументы.

Изменения в обработке integer

1. Некорректные восьмеричные числа будут выдавать ошибку компиляции:
      $i = 0781; // 8 - неверный разряд для восьмеричного числа


Ранее все после некорректного разряда отбрасывалось, и в $i была бы 7.

2. Побитовые сдвиги на отрицательные числа теперь бросают ArithmeticError:
      var_dump(1 >> -1);
      // ArithmeticError: Bit shift by negative number


3. Сдвиг влево на число, большее разрядности, вернет 0:
      var_dump(1 << 64); // int(0)


Ранее поведение зависело от архитектуры, на x86 и x86-64 результат был == 1, т.к. сдвиг был циклическим.

4. Аналогично сдвиг вправо даст 0 или -1 (зависит от знака):
      var_dump(1 >> 64);  // int(0)
      var_dump(-1 >> 64); // int(-1)


Изменения в обработке ошибок

1. Больше не парсятся в числа строки с шестнадцатиричными числами:
      var_dump("0x123" == "291");     // bool(false)     (ранее true)
      var_dump(is_numeric("0x123"));  // bool(false)     (ранее true)
      var_dump("0xe" + "0x1");        // int(0)          (ранее 16)

      var_dump(substr("foo", "0x1")); // string(3) "foo" (ранее "oo")
      // Notice: A non well formed numeric value encountered


filter_var() может использоваться для проверки строки на содержание шестнадцатиричного числа или конвертации в обычное число:
    $str = "0xffff";
    $int = filter_var($str, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
    if (false === $int) {
        throw new Exception("Invalid integer!");
    }
    var_dump($int); // int(65535)


2. Из-за добавления эскейп-синтаксиса для юникода, строки в двойных кавычках и heredoc должны это учитывать:
      $str = "\u{xyz}"; // Fatal error: Invalid UTF-8 codepoint escape sequence


Необходимо двойное экранирования слэша:
      $str = "\\u{xyz}";

Хотя простое "\u" без последующей { не затронуто, и вот так заработает без изменений:
      $str = "\u202e";


Про новую обработку ошибок уже здесь писали, не буду повторяться. Из интересного еще можно добавить, что конструкторы встроенных классов теперь всегда кидают исключения, тогда как ранее некоторые просто возвращали null.

Из других изменений документ отмечает теперь отсутствие $this для нестатических методов, вызванных статически (ранее метод использовал $this вызывающего контекста).

Пополнился, что логично, список недоступных для классов, трейтов и интерфейсов имен — добавлены bool, int, float, string, null, false, true, а также для будущего использования: resource, object, mixed, numeric.

Конструкт yield не требует больше скобок при использовании в выражениях. Он теперь право-ассоциативный оператор с приоритетом между «print» и "=>". Поэтому поведение может измениться:

      echo yield -1;
      // ранее интерпретировалось как
      echo (yield) - 1;
      // а сейчас как
      echo yield (-1);


      yield $foo or die;
      // ранее интерпретировалось как
      yield ($foo or die);
      // а сейчас как
      (yield $foo) or die;


Эти случаи рекомендуется принудительно уточнять скобками.

Из заметных изменений в стандартной библиотеке функций отмечу только удаление call_user_method() и call_user_method_array(), остальное не столь значительно (выкинули dl() в php-fpm, переименовали и оптимизировали zend_qsort -> zend_sort, добавили zend_insert_sort, немного поменяли поведение setcookie при пустом имени cookie и убрали фатальную ошибку ob_start внутри буферизации).
JSmitty @JSmitty
карма
6,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (62)

  • +2
    Ура, товарищи! Хотелось бы увидеть тесты производительности на популярных cms и фреймворках.
  • 0
    Ура! Осталось подождать всего-то тыщу лет, пока эта версия докатиться до продакшн-серверов и виртуальных хостингов.
    • +3
      Мне кажется, что переползание начнётся сразу по релизу, уж очень соблазнительно получить прирост производительности даром. Конечно, если не выплывет какого-нибудь крупного косяка. На мой взгляд, изменения менее существенны, чем при переходе с 5.2 на 5.3.
      • 0
        Не забывайте о куче pecl и zend расширений, которые многим нужны.
      • +2
        На мой взгляд, изменения менее существенны, чем при переходе с 5.2 на 5.3.
        Менее существенны в смысле принципиальности и глобальности, но переписывать надо достаточно много кода, ряд проектов мы просто не будем переводить на 7-ку, ибо никто это не оплатит — слишком дорого.
        Было бы неплохо, что бы появился какой-то анализатор кода, который будет указывать на то что надо поправить, но надежды на это немного.
        • +4
          • 0
            Вроде ж планировали еще и автоматический фиксер кода, нет?
        • –1
          Да вроде особо нечего переписывать-то. Надо очень странный код писать, чтобы изменения его затронули. У меня все юнит-тесты прошли на альфе. Правда, opcache иногда сегфолтился, но это другая проблема.
          • +2
            unset внутри цикла никогда не делали?
            • 0
              Кстати, насколько я понимаю, unset(), сделанный внутри цикла, сработает все равно. Просто эти изменения будут не видны внутри цикла. А снаружи — пожалуйста. Или нет?
            • +2
              А это, как уже верно заметил FractalizeR, ложь и провокация. Все работает, как и прежде:

              codepad.viper-7.com/gT7CyS

              Только что проверил на свежем master, результат тот же. Копия используется «внутри».
    • +4
      Докатится.
    • 0
      Осталось подождать всего несколько месяцев. Ведь сейчас все и так на 5.6 работает.
      За исключением ультрадешевых проектов на shared hosting.
  • +1
    А в этой версии название функций привели к единому стилю, порядку аргументов?
    • –3
      Да вряд ли приведут. Это поломает огромное количество существующего кода, а тогда PHP7 нафиг никому не нужен будет.
      От него и сейчас то все уходят.
      • –2
        От него == от PHP5
      • +2
        Есть на это RFC и совместимость он не ломает. Старые названия функций остаются и добавляются новые более однородные названия. В 7.0 он уже не войдет, но в какой-нибудь релизе 7.1+ вполне могут что-то такое сделать.
        • –3
          Т.е. они добавят ещё «стопицот» новых функций? Ужас. Читать чей нибудь PHP код вообще будет невозможно.
          • +3
            Добавят, а стопицот старых объявят устаревшими и выпилят через пару релизов. А вы как хотели поменять имена и сохранить обратную совместимость одновременно?
            • –9
              Никак. У них не получится, и никакие RFC не помогут.
              Учитывая средний уровень PHP программистов, то никакие RFC и best-practices они читать не будут. Напишут код как нибудь, чтобы работало: насобирают кусков кода со stackoverflow и остального интернета. Это будет каша из старых функций, и новых. И такого кода будет не мало. И опять в кругах разработчиков PHP вспыхнут прения: ломать совместимость и удалять старые функции, или нет. И, в общем-то, я думаю Вы и сами понимаете какой выбор они сделают. Такой же, как на протяжении всех предыдущих лет.

              • +2
                Факты говорят иное. Например, mysql_ функции в 7.0 уже выпилили ровно таким же способом, и с 7й версии никакой каши mysql_/mysqli_ уже не будет.
                • 0
                  Время покажет.
              • +1
                Плохие средние PHP программисты в состоянии перепроверить порядок $haystack, $needle, а вы нет?
                • 0
                  Какая у Вас интересная логика. Во-первых, про себя я ничего не говорил. Во-вторых, почитайте хотя бы это. Кажется, на хабре даже где-то был её перевод. Сразу поймёте масштаб проблемы.

                  Cразу видно, что с крупными проектами Вы не работали. Если PHP комьюнити решится на такой шаг, то я с удовольствием посмотрю, насколько тяжело комьюнити дастся такой переход. За примерами далеко ходить не надо: Python'у, который чуточку менее популярный чем PHP, переход со второй версии на третью дался с большим трудом — до сих пор очень большое количество пакетов не поддерживают третью версию, а многие даже не собираются переписывать свои библиотеки.
                  • +1
                    Так не надо переписывать старые проекты и проблем не будет. Всего-то нужен LTS* релиз на основе последнего PHP 5 и всё, года через три-четыре будем вспоминать как ужасно раньше было :) (популярные библиотеки обновятся рано или поздно, а даже если что-то умрет, то большая любовь комьюнити к велосипедостроению довольно быстро заполнит освободившиеся ниши).

                    * так то уже всё есть www.zend.com/en/support-center/support/php-long-term-support, но на платной основе.
                    • 0
                      Про Python так же говорили. Как итог — я сейчас регулярно натыкаюсь на библиотеки, которые до сих пор не портированы на python3, и вряд ли будут. И ещё раз повторяю: таких библиотек очень много до сих пор. Есть люди, которые решили определённые задачи написав библиотеку, и выложили её в публичный доступ. Дальше они поддерживать их не будут, и вряд ли перепишут, а комьюнити, не факт что перепишет лучше. Пусть даже перепишет, вопрос во времени, которое им понадобится.

                      Давайте мы не будем гадать. Я Вам привёл пример языка, где подобный переход (хотя на самом деле, между python2 и python3 разница не такая большая, как планируемая между PHP5 и PHP7) длится уже 8 год, а в случае с PHP всё гораздо хуже, как из-за большего количества функций языка, так и из-за среднего уровня людей на нём пишущих приложения. Можете мне рассказать success story хоть одного языка, в котором подобное прокатило? Да, наверняка останется достаточное количество достойных программистов, фанатов языка которые всё напишут. Вопрос только в том, сколько времени у них это займёт, и сколько будет написано жуткого кода во время этого периода.

                      Так вот, отходя плавно от полемики в которую Вы меня пытаетесь увести: разговор был про переписывание функций, которое планируется к следующим релизам. Вопрос: сколько будет написано пахнущего кода во время этого перехода? А во время следующей «чистки»? И ведь кому-то это придётся поддерживать.
                      • 0
                        Вообще то с точки зрения библиотек, PHP5.6 и PHP7 не отличаются практически, потому что обратная совместимость сохранена на уровне языка. Обратная совместимость потеряна только на уровне внутренностей реализации интерпретатора. А в питоне2 и 3 потеряна обратная совместимость на уровне языка. Так что вы что-то не то сравниваете.
                        • –1
                          Подождите. Вы опять пытаетесь меня увести в сторону. Я изначально написал:

                          Т.е. они добавят ещё «стопицот» новых функций? Ужас. Читать чей нибудь PHP код вообще будет невозможно.


                          Т.е. планируется добавить пачку новых функций, которые повторяют функционал других. Меняется только название и порядок/количество аргументов. Будет снова гора кода, где часть функций deprecated, а часть «новые».
                          Вообще этот ужас с количеством функций языка удручает.

                          В python особо не потеряна обратная совместимость. Не так много на самом деле изменилось. Ну, это так по мне. Для меня переход с 2 на 3 версию дался легко.
                          • +1
                            Не очень понимаю чем может удручать поэтапное эволюционирование языка?

                            Я же указал вам на что у сообщества PHP не будет проблем с переходом популярных(и не очень) библиотек на новые версии языка, так как нет потери обратной совместимости, в отличии от питона, пример которого вы привели.

                            Что касается кода, не высокого качества, то его количество не измениться. Но хороший код станет писать приятнее и удобней.
                            • +2
                              революция vs эволюция всегда было холиварной темой.

                              Что до функций — на самом деле не проблема нынче написать тулзу для автоматической миграции кода. Благо инструментарий для основы имеется.
                      • +2
                        > между python2 и python3 разница не такая большая, как планируемая между PHP5 и PHP7

                        Python не знаю, к сожалению, но судя по первым ссылкам в гугле (например,http://pythonworld.ru/osnovy/python2-vs-python3-razlichiya-sintaksisa.html) там вообще всё сломали и сделали по другому. PHP7 же это просто эволюция при том больше самого движка нежели синтаксиса (из того что выше не так много по настоящему критичного). Планируемое переименование функций проблем тоже не создает ибо можно автоматизировать генерацию алиасов на старые функции (я вообще не думаю что кто-то будет руками сидеть и все это переносить). Да и вообще переход с PHP4 на PHP5 был гораздо хуже.
                  • +1
                    Имею достаточно опыта в разработке и в руководстве личным составом разработчиков, чтобы сделать вывод.
                    Проблемы от $haystack, $needle встречаются чуть чаще, чем никогда. Чуть больше проблем с другими неоднозначностями и тонкостями языка. Но это даже не рядом с проблемами, когда программист плохо знает фреймворк, когда вытаскивает и перебирает всю таблицу из базы, когда забывает, что сделали аякс доставку части контента. Когда сваливает весь функционал в один обработчик события.

                    Главные проблемы не в языке, а в методичном следовании универсальным антипаттернам.
                    Статью же от белок истеричек оставим белкам истеричкам.
              • –1
                > «ломать совместимость и удалять старые функции, или нет»

                В таких случаях, так и напрашивается конструкция вида:

                  use PHP5.3;
                
                • +3
                  Нет уж, спасибо, не надо. Кушайте это в перле.

                  Если для конкретного проекта нужен php 5.3, никто не мешает его установить.
                  • +1
                    «Мусье не знает толк в извращениях» (ц)
      • –4
        PHP, наверное, хороший язык и вы все его любите. Но, черт, какой-же он нечитабельный. Сейчас работаю с одним проектом, у которого сервер написан полностью на PHP (программистами, которым пофиг на хорошие практики) и разобраться с ходу ни в чем не получается. Вообще интересно было бы почитать какой-нибудь материал про то, как писать maintainable PHP. Может кто-нибудь что-нибудь посоветует? Чем короче публикация — тем лучше. :)
        • +5
          Казалось бы, дело не в языке, а людях, пишущих код. Тем более вы сами про это же и пишете.
          • –6
            Когда в языке есть 10+ способов сделать одно и тоже, и выстрелить себе в ногу — это конечно способствует написанию хорошего кода. :)
            • –1
              Господа минусующие: у Вас есть что возразить по делу, или продолжите сливать мне карму, просто от обиды из-за фактов? Факты штука такая, против них не попрёшь.
              • +2
                Я вам не минусовал, но все же комментарий получается однобоким: если выкинуть все ЯП, позволяющие выстрелить в ногу и сделать что-то более, чем одним способом, то много ли останется чего-то приемлемого?
                Понятно, что если код пишут люди, которым плевать на стандарты, практики и подходы к разработке на конкретном ЯП — то тут уже ничего не поможет (включая возможности и инструментарий самого ЯП).
        • 0
          Вы ошиблись: этот вопрос надо не мне задавать. У меня после написания кода на PHP остались сугубо негативные чувства, и я расскажу больше плохого, чем хорошего.
          • 0
            А знаете, расскажите. Только постарайтесь объективно. + уточните что и когда вы писали, насколько хорошо его знаете и на чем пишите в основном. Так, для статистики. Хотя бы на примере вот этого.
            • 0
              Заодно расскажите (я может плохо знаком с telnet или были другие особенности), почему вы из сокета читаете по одному чару?
        • +1
          Ну так можно любой язык объявить нечитабельным с колокольни «знания» другого.
  • +2
    В целом отличные изменения, кроме вот этих двух вещей, который вызывает только вопрос «ШТА?!»

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

    Господа, как с этим жить? Теперь трейсы перестанут быть трейсами т.к. вызов функции с аргументами, показанными в трейсе, не всегда будет приводить к оригинальному результату.
    Как итог, трейсам нельзя будет доверять. Как могли на такое пойти? Это же просто суицид-фича какая-то.

    image

    Далее

    Во время итерирования массивов по значению, foreach теперь пользуется копией массива, и его изменения внутри цикла не повлияют на поведение цикла

    Когда итерируются массивы по ссылке, изменения в массиве будут влиять на цикл


    Тоже неоднозначное изменение, которое в корне меняет концепцию foreach — если раньше это был обычный обход элементов массива либо по значению, либо по ссылке, то теперь это 2 совершенно разных вида циклов: один read-only, второй read-write.
    В принципе, ничего не имею против, но стоило ли ради этого ломать обратную совместимость, даже если это касается такой странной практики, как изменение массива в момент его обхода?
    • +1
      А что странного в этой практике? Конечно, есть альтернативные способы написания, но удаление ненужных по какой-то логике элементов из массива при итерации вроде бы ничем не странное
    • +1
      foreach не делает исходный массив read-only. Изменения, произведенные над массивом внутри цикла будут видны после выхода из цикла. Можно пробовать, например, здесь: codepad.viper-7.com/pJ6KCf

      <?php
      	$array = [1, 2, 3];
      
      foreach($array as $arg) {
        unset($array[0]);
      }
      
      var_dump($array);
      


      Выведет массив из двух элементов, а не из трех.
  • 0
    Strict Standards: Only variables should be passed by reference
    Я, конечно, за максимальный стрикт, но зачем он здесь нужен?
    Ну передали результат «по-ссылке», не сохранится он нигде после вызова, ну и что?
    Нет, надо городить промежуточные переменные.
    • 0
      Это указание, что, вероятно, в коде написано не то, что хотел написать разработчик. Это как присвоение в if: можно (в некоторых ЯП), но часто не то, что нужно.
      • 0
        Ну почему, в примере вполне реальный случай. Получить из функции массив, а из него только последний элемент.
        • 0
          Мы же сейчас про array_pop? Но ведь это не получение последнего элемента, это забирание элемента с вершины стека. Передавая по значению, теряется вообще смысл операции.
          • 0
            Это: 1. удаление элемента из стека, 2. получение этого элемента.
            Меня может интересовать только этот элемент, а состояние остального стека нет.
            • 0
              В таком случае да, это не то, что нужно. Подошел бы end, но и он по ссылке хочет значение. Тут скорее вопрос к полноте и функциональности стандартного набора функций и ЯП, раз приходится писать свой вариант arr[count(arr) — 1] за его неимением.
              • 0
                по ссылке end хочет массив только потому что меняет указатель на текущий элемент списка (массивы же в php все еще списки). Не вижу в этом ничего такого, из-за чего нужно писать свою функцию, тем более покрывающую только часть сценариев. В то же время end влияет только на случаи когда код зависит от указателя на текущий элемент. Не забывайте что может быть и такое:

                $arr = [1, 2, 3];
                $arr[7] = 4; // мало ли, бывает
                
                $end = end($arr); //4
                $arr[count($arr)-1]; // Notice:  Undefined offset
                
                • 0
                  Так я про end к тому, что он тоже позволяет получить элемент, но тоже с тем же подходом со Strict Standards. Т.е. в php есть две более-менее подходящие функции, но обе хотят ссылку. И ни одной, которым ссылка не нужна.
                  • 0
                    ясно, тут соглашусь. Хотя опять же, основное предназначение функции end — двигать указатель. Если вам так хочется получить последний элемент массива и не хочется париться из-за побочных эффектов то можно воспользоватьяся тем что пых копирует аргументы при передаче в функции (при записи, или в нашем случае, создании референса).

                    function last(array $arr) {
                        // слава Copy-on-write
                        return end($arr);
                    }
                    
                    // лучше вообще юзать какие-нибудь коллекции
                    function first(array $arr) {
                        return reset($arr);
                    }
                    
                    function foo() {
                        return [1, 2, 3, 4];
                    }
                    
                    last(foo()); // 4
                    first(foo()); //1
                    

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.