Обновление PHP до 7.0.6 может «сломать» ваш код

    Внимание! Будьте осторожны при обновлении PHP до версии 7.0.6! В этой версии был исправлено несколько важных багов, на которые мог неявно полагаться ваш код или код вашего фреймворка.

    PHP bugs



    Какие баги были исправлены?


    bugs.php.net/bug.php?id=62059
    bugs.php.net/bug.php?id=69659
    bugs.php.net/bug.php?id=71359

    Тестовый пример:


    Самый лаконичный можно взять из описания последнего бага ( www.pastebucket.com/97499 в оригинале )

    class Test
    {
        protected $attributes = [
            'attribute1' => 'value1',
        ];
    
        public function __get($name)
        {
            print "GET\n";
            if (array_key_exists($name, $this->attributes)) {
                return $this->attributes[$name];
            }
    
            trigger_error("Property $name does not exist");
            return null;
        }
    
        public function __isset($name)
        {
            print "ISSET\n";
            return array_key_exists($name, $this->attributes);
        }
    }
    
    
    $obj = new Test();
    
    var_dump($obj->attribute1 ?? 'default');
    
    //GET
    //string(6) "value1"
    
    var_dump($obj->attribute2 ?? 'default');
    
    //GET
    //PHP Notice:  Property attribute2 does not exist in /var/www/html/test.php on line 23
    //string(7) "default"
    


    А можно проще?


    Да.

    В определенных случаях при использовании конструкций языка isset() и empty() не использовался вызов магического метода __isset() при его наличии в классе, а вместо этого сразу вызывался __get() и решение принималось на основе того, что он вернет.

    Теперь это исправлено. Можно надеяться, что во всех случаях применения isset() или empty() на «магические» свойства или «магические» ключи массива-объекта ArrayAccess будет сначала вызываться __isset().

    Чем это грозит лично мне?


    Тем, что если в вашем классе есть метод __get(), но нет метода __isset(), ваш код «сломается». Отныне во всех таких случаях isset() будет всегда возвращать false, а empty() === true

    Что делать?


    Я предпочел бы откатиться до 7.0.5, а затем аккуратно отрефакторить код. Вряд ли у вас много мест, где есть __get(), но нет __isset(). Впоследствии было бы неплохо добавить в ваш анализатор кода соответствующее правило и запретить коммит такого токсичного кода.
    Поделиться публикацией
    Похожие публикации
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 28
    • +12
      Тем, что если в вашем классе есть метод __get(), но нет метода __isset(), ваш код «сломается»

      если у вас такой код, то он уже сломан
      • 0
        Поискал по vendor и увидел, что много где такие проблемы. Печально.
      • +4
        спасибо за 3 вещи:
        1. за то, что напомнили про то, что вышла 7.0.6
        2. за то, что вкратце описали ключевую вещь, а то сначала бы поставил(пусть и в dev), а потом прочел, как обычно.
        3. за то, что косвенно помогли оптимизировать мой говнокод — залез на свежую голову, сделал человеческую проверку.

        • –3
          • –3
            От empty() пора избавляться, ИМХО.
            • 0
              альтернативу не подскажите?
              • +3
                Я соглашусь с товарищем выше, пусть и не в такой категоричной форме.

                Всегда приятнее видеть в коде что-то вроде null === $value или 0 == $value вместо empty($value)

                Явное лучше неявного в любом языке. К PHP это относится особенно.
                • 0
                  по-моему, дело вкуса…
                  • 0
                    Пока вы волк-одиночка — да, для вас это будет дело вкуса.
                    Однако в любой более-менее крупной команде есть стиль кода. И несоблюдение этого стиля чревато выпиливанием из команды.
                    • +1
                      Если вы — в команде, и в этой команде есть «стиль кода», то о чём тогда тут вообще говорить? :)

                      По-моему, либо ты в команде и, соответственно, подчиняешься правилам — и тогда нет никаких проблем и поводов для дискуссии о том, от чего надо избавляться, либо ты не в команде — и в этом случае тоже нет причин для споров. :)

                      P.S. На мой взгляд — да, «явное лучше неявного». Даже когда я не в команде… :)
                      • 0
                        Однако в любой более-менее крупной команде есть стиль кода.

                        писать всегда только явные проверки или только empty это плохой "стиль кода" команды. Это даже не стиль кода, это способ оформления логики и выражения мысли, а в этом плане важна гибкость.


                        Я поступаю проще. За "стиль кода" у меня отвечают статические анализаторы и фиксеры кодинг стайлов, и мне в принципе плевать что использует разработчик в своем проекте (в рамках моей команды) пока он покрывает это тестами и знаете что делает.

                    • +4
                      Хм. Наоборот недавно переучился писать empty() и is_null(), вместо === null или == 0, или еще хуже !$value
                      Думаю, однозначного ответа как лучше нет
                      • +1
                        empty() это удобный синтаксический сахарок прежде всего:

                        empty($array[$key]) ~ !isset($array[$key]) || $array[$key] == false
                        • +1
                          Мне кажется, что empty гораздо удобнее и логичнее использовать в различных ситуациях. Да и просто приятнее написать (!empty($foo)), нежели ($foo !== null || $foo != ""), хотя делают оба варианта одно и тоже.

                          Так же само мне приятнее использовать (!empty($array)) при работе с массивами, нежели получать количество его элементов, для сравнения, вроде (count($array) == 0).

                          empty — это возможность понять, есть ли в переменной, что-то важное и если нет — не обращать на нее внимания. Тогда, как прямое сравнение — это возможность убедится что переменная не является null (хотя по прежнему может быть «ненужной», например содержать пустую строку или 0 (если это целое число)).
                          Я хочу сказать что нужно действовать по ситуации, если переменная может быть равна пустой строке (если это допускается приложением), но не может быть равна null — Вам необходима прямое сравнение, если необходимо проверить наличие в переменной данных, для дальнейшего использования — я не вижу смысла писать условие, в котором перечислены все возможные исключающие ситуации, а проще воспользоватся функцией которая именно для этого предназначена.

                          Лаконичнее же, данное решение, мне кажется из-за естественности написания. Вы ведь согласитесь, что «если массив не пустой то...» выглядит куда более естественно, нежели «если количество элементов в массиве равно нулю» уже как-то не очень.

                          Использовать можно и тот и другой вариант, по поводу же стайл-гайдов, то я не думаю, что есть хоть одна группа разработчиков, запрещающая использовать функцию empty (как нативный инструмент языка), своим программистам — это (использования тех или иных функций), собственно, на мой взгляд вообще не должно входить в стайл-гайды.
                          • –1
                            Мне кажется, что ваш длинный и, безусловно, ценный комментарий немного не по теме статьи.
                            • +1

                              Зато по теме ветки комментариев.

                          • 0
                            Так empty — это не только проверка на 0 и null…
                      • +1
                        7.0.5 тоже бажный

                        https://bugs.php.net/bug.php?id=71948
                        • –1
                          Variables by reference with goto


                          Oh, really?

                          Да, разумеется, это баг. Я не спорю. Но степень его экзотичности на два порядка больше, чем у того, что описано в статье. По крайней мере я не видел ни одного production-ready фреймворка, использующего goto.
                          • +1
                            меня зацепило вот этим https://github.com/zendframework/zend-code/issues/49
                          • 0
                            symfony2 https://github.com/symfony/symfony/blob/f29d46f29b91ea5c30699cf6bdb8e65545d1dd26/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php#L271
                            • +1
                              ни одного production-ready фреймворка, использующего goto

                              в любой реализации конечного автомата (парсеры например) можно наткнуться на goto. Другой вопрос что бага никак не связана с goto, а скорее сломали свитч: https://bugs.php.net/bug.php?id=71914


                              Опять же проблемы будут только у тех кто по каким-то причинам очень любит ссылки.

                          • 0
                            Полезная статья. Спасибо автору. Сделал английский перевод статьи.
                            • –1

                              Какой смысл было публиковать ссылку на хабре а не на рэддите том же? Там выхлоп был бы посущественнее, там же обитают core-разработчики пыха.

                              • +1
                                Ссылку привел не ради пиара (кому интересно читать тот же материал на другом языке?), а с целью ознакомления: возможно кто-то захочет поделиться данной информацией с зарубежными коллегами.
                                • 0

                                  И потому я и говорю — поделитесь ссылкой на реддите от своего имени.

                                  • 0
                                    Благодарю Вас за совет. Уже сделал.

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

                            Самое читаемое