Обзор моих любимых фич PHP7

Original author: Jody LeCompte
  • Translation


Когда люди обсуждают изменения в PHP7, самое распространенное что вы слышите — это значительно улучшенный движок, который может похвастаться более быстрой скоростью выполнения и значительно меньшим объемом памяти при сравнении обычных приложений PHP, таких как Drupal, WordPress и MediaWiki.


Не поймите меня неправильно, это все конечно здорово! Мне удалось перенести несколько устаревших приложений CodeIgniter на PHP7 и достигнуть гораздо более высокой производительность с небольшими изменениями в кодовой базе. Тем не менее, PHP7 также добавляет несколько новых функций, которые могут помочь оптимизировать существующий код или повысить качество написания нового кода. Здесь я изложил несколько моих избранных фич.


Скалярный параметр и возврат заявленного типа


PHP имел объявления типов и до 7 версии, но ограничивался только объектами и массивами. PHP7 теперь обеспечивает поддержку всех скалярных типов и предлагает два разных объявления типов.


Принудительный:


это тип объявления по умолчанию и просто означает, что среда выполнения PHP будет пытаться приводить значения, когда это необходимо. Возьмем, к примеру, следующий код.


<?php
function reverseString(String $str) : String 
{
    return strrev($str);
}
print(reverseString(1234));

Мы указываем, что параметр $str должен иметь тип String а также возвращаемое значение также должно иметь тип String. Поэтому, когда мы передаем число 1234, оно принудительно переводится в строку "1234" и переводится без ошибок.


Строгий:


Второй, строгий тип, включается с помощью флага, добавленного в начало каждого файла. Когда он включен, то интерпретатор не приводит тип, как в приведенном выше примере, он отвечает ошибкой и останавливает выполнение скрипта.



<?PHP
declare(strict_types = 1);
function  reverseString(String  $str): String 
{
    return  strrev($str);
}
print (reverseString(1234));

Добавив единую declare инструкцию в самом начале файла, в тот же код, что и раньше, теперь мы получаем следующее сообщение об ошибке:


Fatal error: Uncaught TypeError: Argument 1 passed to reverseString() must be of the type string, integer given

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


Null Оператор ??


В отличие от некоторых языков, где вы можете использовать имя переменной в качестве выражения в выражении if и смело предполагать, что если значение не определено или пустое, то значение будет false, PHP же будет бросать ошибку о неопределенной переменной, индексе и т. д. Это делает очень многословным, обычный код, с использованием if, чем другие языки, как например, в следующем примере.


 <?php
if(!isset($_GET['key'])) {
    $key = 'default-value';
} else {
    $key = $_GET['key'];
}

Даже при использовании тернарного оператора необходима функция isset. С новым null оператором ?? вы можете существенно облегчить код:


 <?PHP
$key = $_GET['key'] ?? 'default_value';

Такое использование еще более эффективно в случаях цепочной проверки, требующих одного или несколько других операторов if.


 <?php
if (isset($_GET['key']) {
    $key = $_GET['key'];
} else if(isset($_POST['key'])) {
    $key = $_POST['key'];
} else {
    $key = 'default value';
}

// Versus

$key = $_GET['key'] ?? $_POST['key'] ?? 'default value';

Маленькое дополнение: Если вы работаете с JavaScript, вы можете делать такие вещи:


const value = 0 || false || 'hello';
console.log(value); // hello

Это не будет работать в PHP, и эквивалентный код на PHP установит значение 0, поскольку новый оператор работает только с null значениями.


Групповые Use Declarations


В предыдущих версиях PHP вы могли импортировать только один элемент (класс, функцию, константу) из определенного пространства имен в одном выражении с использованием объявления use. Это часто приводило к очень повторяющемуся коду, такому, как в примере ниже..


<?php
use VendorName/LibraryName/ClasName1;
use VendorName/LibraryName/ClasName2;
use VendorName/LibraryName/ClasName3;

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


<?php
use VendorName/LibraryName/{ClasName1, ClassName2. ClassName3};

Константные массивы


Именованные константы — очень ценный инструмент в PHP. Одним из распространенных вариантов использования является улучшение читаемости кода путем предоставления семантических имен произвольным данным, таким как цвета, значениям RGB или магическим номерам в коде, которые неоднозначны и могут сбить с толку в других случаях.
Любой, кто работал с PHP долгое время, скорее всего, видел приложение с файлом констант (или даже несколькими файлами), которое содержит десятки, если не сотни именованных констант, требующих длинных и описательных имен, чтобы избежать конфликтов имен.


<?php
define('COLOR_RED', '#f44141');
define('COLOR_BLUE', '#4286f4');
define('COLOR_GREEN', '#1ae01e');
define('COLOR_PURPLE', '#f309f7');
define('COLOR_ORANGE', '#ef7700');

Именованные константы, в дополнение к ранее поддерживаемым типам данных, могут быть как индексированным так и ассоциативным массивом. Это поможет более точно сгруппировать многие именованные константы, которые могут быть у вас в вашем приложении.


<?php

// В качестве ассоциативного массива

define('COLORS', [
    'red' => '#f44141',
    'blue' => '#4286f4',
    'green' => '#1ae01e',
    'purple' => '#f309f7',
    'orange' => '#ef7700',
]);
echo(COLORS['red']); // #f44141

// Как индексированный массив

define('COLORS', [
    'red',
    'blue',
    'green',
    'purple',
    'orange',
]);
echo(COLORS[0]); // 'red'

Вывод


Есть еще несколько замечательных новых функций, о которых я не сказал, таких как анонимные классы и оператор spaceship. Так что определенно проверьте документацию на PHP.net для получения дополнительной информации. Спасибо, что нашли время, чтобы прочитать все это и, пожалуйста, оставляйте любые вопросы или комментарии ниже.


спасибо berez за замечания.

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 55

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

    необходима функция isset

    Всегда предпочитал !empty().
      0
      image
        +2
        Я в курсе. К чему эта портянка? Вы не поняли, где у вас в тексте ошибка?

        смело предполагать, что если значение… пустое, то значение будет false
        функция isset

        isset() на пустую строку, false, число 0, строку '0' даст true.
        Эта функция не для проверки значения, она для проверки факта существования переменной.
        Тщательне́е надо.
          +1
          Все он правильно сказал, все что вы перечислили не пустые значения
            +1
            «Нет пустого значения, кроме NULL, и taliban пророк его.»
              0
              Собственно, NULL — это даже не пустое значение, это отсутствие значения вообще. Даже пустого.
                0
                А void? =)
                  0
                  var_dump'ом посмотри́те. :)
            0
            Ой не пойму я, чего вы спорите, берем и пишем:
            $arr = ['', 0, '0', false, NULL];
            echo "Num\tEmp\tSet\tNul\tBool\n";
            for($i = 0; $i < 5; $i++) {
                echo $i, "\t", empty($arr[$i]), "\t", isset($arr[$i]), "\t", is_null($arr[$i]), "\t", boolval($arr[$i]), "\n";
            }
            

            смотрим на вывод и делаем выводы.

            ЗЫ. Если лень заморачиваться: с одной стороны, NULL означает отсутствие значения, с другой, мы можем написать $a=NULL. Это значит, что значение может быть установлено, но оно все равно будет неопределено. Даже более простой случай со строкой '0' заставляет нас помнить о контексте использования: если работаем со строкой, то это эквивалентное true значение, а если с числами, то false. В общем, нельзя расслабляться, даже в мелочах.
          +1
          Всегда предпочитал !empty().

          Не подходит, когда значение может быть 0, false и т д
            +1
            Да, но подходит для того что описал автор:
            значение не определено или пустое
            что собственно и процитировал 027 в своём первом комментарии.
            Причём под этой фразой автор проверяет только на «определено» isset(), но не проверяет на «пустое» (перевод — правильный, в оригинале — «the value isn’t defined or is empty», значит ошибку допустил автор в коде или его описании), нужно isset($x) && $x, на что 027 и ответил, что в таких случаях предпочитает использовать !empty(), что вполне логично, так многие и делают.
              0
              @27 вырвал фразы из разных контекстов, автор пишет:

              Даже при использовании тернарного оператора необходима функция isset


              т е имеется ввиду что-то вроде такого:

              $value = isset($_GET['limit']) ? $_GET['limit'] : 10;


              конечно щависит от ситуации, иногда тебе подойдет и empty, но однозначто это сказать нельзя, поэтому они и неэквивалентны
                +1
                А что значит пустое? 0 пустое значение? А если мне надо число в промежутке от -1 до 1, значит ли это что валидных значения всего два? Ведь ноль пустое значение. Да и фолс, мне нужен ответ человека на вопрос: «курит ли он», бессмысленно хранить его как текст да/нет, бул предпочтительней. К пустому значению с натяжкой можно лишь пустую строку притащить, ито даже это значение может быть вполне себе валидным и полным значением отвечающим требованию.
                Сама фраза пустое значение изначально неправильная. Эти значения не пустые, они вполне себе определенные и ими можно оперировать. Только по этому к нему придираются, а не из-за неправильного перевода или чего бы то еще.
                  0
                  В php всё просто — пустое значение или нет определяется с помощью функции empty().
                0
                Не подходит для чего именно?

                isset() даст true, если переменная существует и ей не присвоено NULL.
                !empty() даст true, если переменная существует, и заодно не NULL, не нуль (целое либо дробное), не символ нуль (string '0'), не пустая строка, не пустой массив, не false.

                То есть, делает примерно то же, что и if внутри скобок. На практике такая проверка нужна часто, а empty() удобна тем, что не генерирует варнинг. И можно не заморачиваться предварительным объявлением переменной. В документации это прекрасно описано. Мне казалось, это даже коты знают, а поди ж ты. :)
                  0
                  Я сейчас не об этом писал, вы вырвали две фразы из разных контекстов, про функцию isset, говорилось в контексте тренарной операции, а не if, и второй момент не подходит если я хочу чтобы значение 0 например присвоилось и не проигнорировалось и взялось дефолтное…
                    0
                    и второй момент не подходит если я хочу чтобы значение 0 например присвоилось и не проигнорировалось и взялось дефолтное…

                    Ничего не понял. Сдаюсь. :)
                      +1
                      $value = empty($_GET['limit']) ? $_GET['limit'] : 10;


                      если $_GET['limit'] будет равно нулю, то $value будет равно 10, но я хочу, чтобы было 0, в этих случаях empty не подходит, а isset — да
                0

                Аналогично. Оно хоть и работало, но спамило в логи, поэтому приходилось использовать empty и т.п.

                +2
                const value = 0 || false || 'hello';
                console.log(value); // hello

                Это не будет работать в PHP, и эквивалентный код на PHP установит значение 0

                Хм…
                const value = 0 || false || 'hello';
                console.log(value);
                VM114:2 hello

                onotole@home:~$ php
                <?php
                $value = 0 || false || 'hello';
                var_dump($value);    
                print_r(phpversion());
                bool(true)
                7.2.7-0ubuntu0.18.04.2

                Жабаскрипт отдает строку хелло, но это его заморочки, по булевой логике должно быть true.
                Что и делает наш любимый седьмой пых. :)
                  +1
                  Я думаю имелось ввиду, что оператор ?? в пхп, почти эквивалентен джсному ||, но пхп проверяет только на null, т е:
                  echo 0 ?? false ?? 'hello';
                  echo null ?? $null ?? 'hello';
                  

                  0hello

                  в джс немного по другому…
                    –1
                    Кстати.
                    <?php
                    $value = 0 ?: false ?: 'hello';
                    var_dump($value); // string(5) "hello"
                    


                    3v4l.org/fFm3U
                      +1
                      да, но если вставить этот вариант в ваш пример то он выдаст нотис:
                      echo null ?: $null ?: 'hello';


                      а в джс вроде отработает как надо

                      Так тчо тут использование по ситуации
                        0
                        ну тогда так)
                        echo null ?: $null ?? false ?: 'hello';
                    0
                    Константные массивы

                    Еще в 5.6 появилось.
                      0
                      Статья запоздала на пару лет как минимум.
                        +9
                        Когда люди обсуждают изменения в PHP7

                        Нормальные программисты давно уже обсуждают изменения в PHP 8.
                        И стараются не использовать define()

                        предлагает два разных объявления типов

                        Никакого «объявления типов» здесь нет. Это рантайм контроль типов.
                        И не «сообщение об ошибке», а исключение.

                        Безграмотная статья.
                          0
                          А что не так с define?
                          • UFO just landed and posted this here
                              +6
                              Совершенно бессмысленная глобальщина же. Код:
                              Заголовок спойлера
                              define('COLOR_RED', '#f44141');
                              define('COLOR_BLUE', '#4286f4');
                              define('COLOR_GREEN', '#1ae01e');
                              define('COLOR_PURPLE', '#f309f7');
                              define('COLOR_ORANGE', '#ef7700');
                              


                              Запросто заменяется на:
                              Заголовок спойлера
                              class Colour 
                              {
                                  public const RED = '#f44141';
                                  public const BLUE = '#4286f4';
                                  public const GREEN = '#1ae01e';
                                  public const PURPLE = '#f309f7';
                                  public const ORANGE = '#ef7700';
                              }
                              


                              О преимуществах второго варианта перед первым стоит упоминать?
                                0
                                Стоит упоминать, что мы не всегда используем классы, или вынуждены использовать define по ряду другим причин? не вижу смысла полностью списывать его со счетов…
                                  +3
                                  «мы» — это кто? И почему «вынуждены»? Кто-то запрещает это делать? Я лично вообще не помню когда последний раз писал 10ти-строчные скриптики, где классы были бы просто оверинжинерингом.
                                    0
                                    Херня, я довольно часто пишу 20-строчные парсеры какой-нить херни, где сам парсер таки класс, и не вижу в этом оверинжиниринга. Просто класс в этом же файле :-)
                                  +1
                                  У второго варианта есть как плюсы, так и минусы.
                                  Ваше предложение, по сути, аналогично предложению заменить isset на empty (см. первый комментарий).
                                    0
                                    define удобен для конфигов. Например DEBUG, ENV.
                                  0
                                  define() работает в рантайме, const — конструкция этапа компиляции. Стоит ли говорить, что второе предпочтительнее?
                                    +2
                                    Вы еще скажите что АОТ лучше чем JIT, только потому что одно заранее все полностью перелопатит, а второе по требованию. Или скажите что lazy loading это полная ересь, и ее использовать ни в коем случае не стоит. Стоит ли говорить что вы глупость сказали?
                                      0
                                      Не передёргивайте пожалуйста. Определение данных и структур во время компиляции, JIT и lazy loading — это как путать тёплое с мягким. Совершенно разные технологии, предназначенные для разного и решающие разные задачи. Это во-первых.

                                      Во-вторых, ваш тон и тон Алекса Леонова черезчур категоричен. Пожалуй, что стоило бы для начала разобраться в чём разница между const и define(). Какие есть плюсы и минусы. Пожалуй, лучше всего ответил на этот вопрос Никита Попов (один из разработчиков ядра PHP): https://stackoverflow.com/a/3193704

                                      В третьих, так да, const определённо лучше, чем define хотя бы потому, что значение константы инлайнится (в пределах файла, где происходит определение константы) и лучше поддаётся оптимизации OpCache благодаря возможности константных вычислений во время компиляции, а не в рантайме, что, допустим, может привести к меньшему количеству опкодов и более эффективному использованию буфера interned strings (не знаю как правильно на русский перевести).

                                      В-четвёртых, в свете реализованных некоторых SSA-оптимизаций конструкция const может (именно может, но не факт) работать опять-таки благодаря константным выражениям и как следствие раскрутки и слопыванию узлов AST.

                                      В-пятых, постарайтесь не быть настолько категоричным и фанатичным. Это я отношу как к вам, так и к Алексу Леонову.
                                        0
                                        Я ни в коем случае не уверждал обратного, я лишь сказал что фраза «Стоит ли говорить, что второе предпочтительнее?» — глупость. Так как не всегда этап компиляции предпочтительней, перед рантаймом. Как ни крутите а код в рантайме исполняется, и пхп при этом нормально себя чувствует. По сравнению с основным кодом количество констант описаных с помошью define крайне мало, и это не затронет производительности (это ведь не основное действие в коде — установка констант?).
                                        А за ссылку спасибо, она явно объективней чем ответ Алекса.
                                  +1
                                  И не «сообщение об ошибке», а исключение.

                                  С каких пор fatal error стало исключением?
                                  тем более, что вы не отловите его с помощью класса \Exception, только \Error ну и \Throwable само собой…
                                    0
                                    Fatal error здесь потому, что исключение не было поймано. Вам никто не мешает его поймать.
                                      0
                                      странные вещи вы пишите, ок вот вам примеры тогда:

                                      \Exception не ловится
                                      sandbox.onlinephpfunctions.com/code/27f7043b880d9cdcc5de067b17243972514b75e1

                                      \Error все ок
                                      sandbox.onlinephpfunctions.com/code/2b97205a8f3cb8a354abeaf9917718484e1210d9

                                      ну и второй момент ексепшен не прерывает выполнение, а фатал — прерывает, можете проверить сами, здесь прерывается…
                                        0
                                        Прерывает только в том случае, если находится в том же файле, где и возникает ошибка. Т.е. вариант:
                                        try {
                                            require __DIR__ . '/any-bad-code.php';
                                        } catch (\Error $e) {
                                            echo $e;
                                        }
                                        
                                        \var_dump('ALL OK');
                                        


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

                                        Т.е. с точки зрения именования любые Throwable/Error — это ошибки с разным уровнем «страшности», а с точки зрения семантики и поведения — это обычные исключения.
                                          0
                                          тот факт, что фаталы, теперь можно отловить в блоке тру кач, не делает их еквивалентными, для этого и есть раздельные классы, чтобы их отловить, почитайте описание, там ясно сказано, что один отловливает ошибки, второй ексепшены…

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

                                          Ну и опять же называть ошибку исключением у меня язык не повернется…
                                            0
                                            Вы меня простите, но у вас каша какая-то в голове. Ничего личного, извините.

                                            Итак, у нас есть интерфейс Throwable. Это общий интерфейс для всех объектов, которые можно выбросить (throw) и поймать (catch).

                                            Далее есть два класса, реализующих этот интерфейс: Error (для исключений системных) и Exception (для пользовательских). От первого наследуется большое количество системных исключений, вроде TypeError, от второго тоже немало, например InvalidArgumentException.

                                            Важно понимать, что Error — это не ошибка. Это класс исключений, который называется «Error».

                                            Что такое ошибки в PHP можно почитать здесь: php.net/manual/ru/errorfunc.constants.php

                                            Почему же мы видим всё-таки ошибку Fatal error? Потому что не поймали исключение! Обратите внимание, что любое исключение, хоть наследующееся от Error, хоть от Exception, если вы его не отловите с помощью catch, всплывет по стеку вызовов до самого верха и вызовет фатальную ошибку.

                                            Вот и всё.

                                            Резюме:
                                            — Error это не «ошибка», а класс исключений, называющийся «Error»
                                            — Любой Throwable можно поймать, в том числе и любой Error
                                            — Ошибки в PHP — не исключения, это совсем другой механизм
                                            — Фатальная oшибка неизбежно возникает, если вы упустили любой Throwable
                                              0
                                              Я прекрасно вас понимаю, и тут дело только в терминологии, и с моей точки зрения, назвать это ошибкой тоже правильно, и вот например если вы откроете описание того же класса \Error, то там как раз таки пишеться про ошибки…

                                              php.net/manual/ru/class.error.php
                                                0
                                                Базовый класс исключений для внутренних ошибок PHP.
                                                Я не вижу никакого противоречия.

                                                Внутри PHP возникает некая ошибка, он создает и выбрасывает исключение соответствующего класса.

                                                Да, согласен, терминология может запутать.
                                          0
                                          Исключение — это не только Exception и его наследники, но всё, что реализует Throwable.
                                      0
                                      Нормальные программисты давно уже обсуждают изменения в PHP 8.

                                      Не подскажете ссылку на почитать? Что-то ничего кроме статей про новый JIT не гуглится
                                    –5
                                    У меня у одного складывается ощущение, что российское широкое сообщество PHP слегка более продвинутое, чем зарубежное?
                                      +3
                                      если дальше русскоязычного хабра никуда не ходить, то именно такое ощущение и сложится.
                                      0
                                      в своё время мне очень помогли константы с ключами
                                        0
                                        Групповые Use Declarations


                                        Надеюсь это не получит широкого распространения, т.к. поиск по проекту без IDE станет невозможным. (А с легаси кодом бывает так, что и с IDE не удается найти использования класса без полного поиска по проекту)
                                          0
                                          Тогда уже не «Null Оператор ??», а «Null coalescing оператор ??».

                                          Only users with full accounts can post comments. Log in, please.