PHP Разработчик
32,8
рейтинг
18 мая 2015 в 15:09

Разработка → Чего ждать, когда ждешь ребенка: PHP 7, часть 2 перевод

Это вторая часть нашей минисерии статей «Чего ждать от PHP7». Читать часть 1

Как вы наверное уже знаете, PHP7 придет в этом году! И сейчас самое время узнать что же нового он нам принесет.

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

Новый экранирующий символ для Unicode


Добавление нового escape-символа \u позволяет нам указывать специфические unicode символы внутри PHP-строк (да-да, те самые emoji и не только).

Синтаксис выглядит так — \u{CODEPOINT}, например, зеленое сердце, , может быть выражено как PHP-строка: "\u{1F49A}".

Оператор объединения со значением NULL (Null Coalesce Operator)


Еще один новый оператор — ??. Он возвращает левый операнд, если он не имеет значение NULL; в противном случае возвращается правый операнд.

Самое главное, что не генерирует notice, если левый операнд является несуществующей переменной. В отличии от короткого тернарного оператора ?:, он работает как isset().

Вы также можете использовать цепочки операторов, чтобы вернуть первый ненулевой из данного набора:

$config = $config ?? $this->config ?? static::$defaultConfig;

Привязка замыканий во время вызова


С PHP5.4 к нам пришли нововведения Closure->bindTo() и Closure::bind(), которые позволяют изменить привязку $this и области вызова, вместе, или по отдельности, создавая дубликат замыкания.

PHP7 теперь добавляет легкий способ сделать это прямо во время вызова, связывая $this и область вызова с помощью Closure->call(). Этот метод принимает объект в качестве своего первого аргумента, а затем любые другие аргументы, которые пойдут в замыкание:

class HelloWorld {
     private $greeting = "Hello";
}

$closure = function($whom) { echo $this->greeting . ' ' . $whom; }

$obj = new HelloWorld();
$closure->call($obj, 'World'); // Hello World

Группировка деклараций use


Если вам когда-либо приходилось импортировать много классов из одного и того же пространства имен, вы, наверное, были очень счастливы, когда IDE делала всю основную работу за вас. Для всех остальных, и для краткости, в PHP7 теперь есть возможность группировать декларирование операторов use. Это позволит быстрее и удобнее работать с большим количеством импортов и сделает код читаемее:

// Original
use Framework\Component\SubComponent\ClassA;
use Framework\Component\SubComponent\ClassB as ClassC;
use Framework\Component\OtherComponent\ClassD;

// With Group Use
use Framework\Component\{
     SubComponent\ClassA,
     SubComponent\ClassB as ClassC,
     OtherComponent\ClassD
};

Группировка может использоваться с константами и импортируемыми функциями, вы можете смешивать все вместе:

use Framework\Component\{
     SubComponent\ClassA,
     function OtherComponent\someFunction,
     const OtherComponent\SOME_CONSTANT
};

Улучшение генераторов


return в генераторах

В генераторах появились две очень интересные возможности. Первая — Generator Return Expressions, позволяющая возвращать значение после (успешного) завершения работы генератора.

До PHP7, если вы пытались что-нибудь вернуть в генераторе, это приводило к ошибке. Однако, теперь вы можете вызвать $generator->getReturn(), чтобы получить возвращаемое значение.

Если генератор еще не завершился или выбросил непойманное исключение, вызов $generator->getReturn() сгенерирует исключение.

Если же генератор завершен, но не объявлен return, то метод вернет NULL.

Пример:

function gen() {
    yield "Hello";
    yield " ";
    yield "World!";

    return "Goodbye Moon!";
}

$gen = gen();

foreach ($gen as $value) {
    echo $value; 
}

// Outputs "Hello" on iteration 1, " " on iterator 2, and "World!" on iteration 3

echo $gen->getReturn(); // Goodbye Moon!

Делегирование генератора

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

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

Это утверждение также справедливо при отправке данных в генератор или выбросе исключений. Они передаются в суб-структуру, как если бы это был ее непосредственный вызов.

Синтаксис такой — yield from <expression>. Посмотрим на примере:

function hello() {
     yield "Hello";
     yield " ";
     yield "World!";

     yield from goodbye();
}

function goodbye() {
     yield "Goodbye";
     yield " ";
     yield "Moon!";
}

$gen = hello();
foreach ($gen as $value) {
     echo $value;
}

При каждой итерации будет выводиться:

  1. «Hello»
  2. " "
  3. «World!»
  4. «Goodbye»
  5. " "
  6. «Moon!»

Стоит упомянуть еще один нюанс: поскольку суб-структуры привносят свои собственные ключи, вполне возможно, что один и тот же ключ будет возвращен за несколько итераций. Недопущение подобного — это ваша ответственность, конечно же, если для вас это важно.

\EngineException


Обработка фатальный и catchable фатальных ошибок в PHP традиционна была невозможна, или по крайней мере очень сложна. Но с добавлением Engine исключений, многие из этих ошибок будут теперь выбрасывать исключение вместо самой ошибки.

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

Эти исключения являются \EngineException объектами, и в отличии от всех пользовательских исключений, они не наследуются от базового класса \Exception. Это сделано специально, чтобы существующий код, который ловит класс \Exception не отлавливал и фатальные ошибки, изменяя свое поведение. Таким образом сохраняется обратная совместимость.

В будущем, если вы хотите поймать как традиционные исключения, так и engine исключения, вам нужно будет отлавливать их новый общий родительский класс \BaseException.

Кроме того, ошибки парсинга в выполняемом функцией eval() коде теперь будут выбрасывать \ParseException, а несоответствие типов приведет к \TypeException.

Пример:

try {
    nonExistentFunction();
} catch (\EngineException $e) {
     var_dump($e);
}

object(EngineException)#1 (7) {
  ["message":protected]=>
  string(32) "Call to undefined function nonExistantFunction()"
  ["string":"BaseException":private]=>
  string(0) ""
  ["code":protected]=>
  int(1)
  ["file":protected]=>
  string(17) "engine-exceptions.php"
  ["line":protected]=>
  int(1)
  ["trace":"BaseException":private]=>
  array(0) {
  }
  ["previous":"BaseException":private]=>
  NULL
}

Скоро!


РНР 7.0.0 исполнилось всего восемь месяцев, и, вполне возможно, это будет самым быстрым релизом мажорной версии в истории PHP. Пока все еще в альфа-версии, но уже сейчас все складывается очень хорошо.

И вы можете помочь сделать еще лучше.

Проверь свой код

Возьмите PHP7 vagrant box от Расмуса и запустите ваши тесты или проверьте по своему чек-листу ваше приложение. Сообщите о багах в проект, повторяйте регулярно :)

Помоги GoPHP7-ext

Одним из основных препятствий для PHP7 является большое количество работы по обновлению всех расширений для работы с новым Zend Engine 3.

Если вы используете расширение, которое не слишком популярно и известно, и вам не нравится уровень его поддержки, или же у вас есть свои собственные расширения — посмотрите на проект GoPHP7-ext и примите участие, перенеся свои расширения на новый движок.

Документация

Каждая новая фича PHP7 имеет свой RFC. Все они могут быть найдены в вики PHP.net и являются хорошей отправной точкой для написания новой документации. Вы можете сделать это онлайн в GUI среде, в том числе и закоммитить (если у вас есть карма) или отправить патч на проверку.

Заключение


РНР 7 будет великим!

Протестируйте ваши приложения. Помогите перенести расширения.

P.S. вы уже пробовали PHP7? Как вы относитесь к нововведениям? Есть ли что-то, с чем не согласны? Когда вы планируете перейти на новую версию? Напишите свои мысли по этому поводу в комментариях.
Перевод: Davey Shafik
Илья Гусев @iGusev
карма
45,5
рейтинг 32,8
PHP Разработчик
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

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

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

  • –10
    Зачем перед именем класса всегда писать \?
  • +6
    лично очень жду именнованные параметры в методах/функциях, надеялся, что, быть может, придут к консенсусу до PHP 7, но к сожалению нет :(
  • +5
    ?? джва десять лет ждал :)
    • –5
      По-мне так просто сахарок. Ничего принципиально нового не вносит. Тренарник + isset() или empty() вполне себе годный для этих целей. Разве что цепочки не так красиво делаются, но цепочки это уже совсем экзотика.
      • +1
        Для красоты всегда хватало

        function is(){
          foreach(func_get_args() as $v){
            if(!is_null($v)){
              return $v;
            }
          }
        }
        
        $value = is(null,null,false,true,1);
        
        • 0
          Дело в isset, а не в проверке на null.
          • 0
            function is(&$args) {
                foreach (func_get_args() as $v) {
                    if (!is_null($v)) {
                        return $v;
                    }
                }
            }
            
            $arr = array('a' => 1, 'b' => false);
            
            var_dump(is($arr['c'], $arr['b'], $arr['a']));
            

            Работает как isset
            • +1
              Ага, почти:

              <?php
              ini_set('display_errors', true);
              error_reporting(-1);
              
              function is(&$args) {
                  foreach (func_get_args() as $v) {
                      if (!is_null($v)) {
                          return $v;
                      }
                  }
              }
              
              $arr = array('a' => 1, 'b' => false);
              
              var_dump(is($arr['a'], $arr['b'], $arr['c']));


              PHP Notice:  Undefined index: c in test.php on line 15
              
              Notice: Undefined index: c in test.php on line 15
              int(1)
              
              • 0
                У меня 5.4.32 версия под винду. Уведомление о несуществующим индексе не выскакивает. Разбираться, почему так происходит, совсем не хочется.
                Проверил на убунте, так же версия из 5.4 ветки, уведомление есть.
                Печаль, что тут сказать. А счастье было близко.
                • 0
                  Разбираться, почему так происходит, совсем не хочется.

                  Ответ содержится прямо в вопросе:
                  версия под винду

                  Используйте идентичное окружение и будет вам счастье.
                  • 0
                    Используйте идентичное окружение и будет вам счастье.

                    Так исторически сложилось — сервера с виндой, Internet Explorer, бюрократия…

                    А убунта личная.
                    • 0
                      <?php
                      
                      function is() {
                          foreach (func_get_args() as $v) {
                              if (!is_null($v)) {
                                  return $v;
                              }
                          }
                      }
                      
                      $arr = array('a' => 1, 'b' => false);
                      
                      var_dump(is($arr['a'], $arr['b'], $arr['c']));
                      


                      дерзай

                      PS сделать надо «default» return значение
                      • 0
                        Если принять тот факт, что первый параметр это «default» значение, то и эта задача решается.

                        function is($default = null, &$args1 = null, &$args2 = null, &$args3 = null) {
                            for ($i = 1; func_num_args() > $i; $i++) {
                                $value = func_get_arg($i);
                                if (!is_null($value)) {
                                    return $value;
                                }
                            }
                            return $default;
                        }
                        
                        $arr = array();
                        $return = is(false, $arr['a'], $arr['b'], $arr['c']);
                        var_dump($return);
                        
                        • 0
                          Чуть чуть PHP5.6

                          function is($default = null, ...$args) {
                              foreach($args as $arg) {
                                   if (!is_null($arg)) {
                                       return $arg;
                                   }
                              }
                          
                              return $default;
                          }
                          
                          $arr = array();
                          $return = is(false, $arr['a'], $arr['b'], $arr['c']);
                          var_dump($return);
                          
                          • 0
                            для 5.6 я так же описывал вариант, несколькими комментариями ниже.
                            Нужно добавить передачу параметров по ссылке. Иначе будут выскакивать уведомление о несуществующем индексе.
                            function is($default = null, &...$args) {
                              ...
                            }
                            
                            


                            Магия…
                • +1
                  А вы запустите мой-то пример вместо своего. У вас там первый и единственный параметр по ссылке передаётся, а вы именно туда отсутствующий подставили, вот и вся разгадка.
                  • 0
                    Вы абсолютно правы. Получилось повторить ошибку. Можно решить эту проблему с кучей входных параметров по ссылкам, с дефолтным значением null. Не слишком элегантно, но можно:

                     function is(&$args1=null, &$args2=null, &$args3=null, &$args4=null, ...) {
                    


                    Ещё вариант для версии 5.6
                    function is(&...$args) {
                    
      • +7
        Да это-то понятно, но тем не менее писать
        $blablabla ?? $default ?? 'None'

        приятнее чем
        isset($blablabla) ? $blablabla : (isset($default) ? $default : 'None')
        • –10
          Если приходится часто писать isset, это что-то не то с архитектурой.
          • +1
            Ну как сказать, внешние данные же принимать как-то нужно.
            В том числе и опциональные. И не всегда можно обойтись указанием $default в $request->param().
            + данные моделей тоже могут быть необязательными.
            • –5
              $request->get('key', 'default')

              Для «не всегда» — проверка на value lambda внутри обработчика default (как в Laravel):
              $request->get('key', function() {
              return result_of_some_computations;
              });
              • +3
                Да, признаю, куда удобнее лепить многословное php-замыкание (небось еще и замкнуть внешние переменные придётся через use), чем написать простой one-liner.
                • –4
                  Удобнее не нарушать инкапсуляцию. Количество кода — странный аргумент, так можно и переменные начать одной буквой называть.
                  • +4
                    Каким образом isset() нарушает принципы инкапсуляции?

                    Не спорю, что красивая архитектура зачастую влечёт за собой большее число абстракций и больше кода, но в данном случае я в упор не вижу, чем пример с замыканием лучше использования isset().
                    А раз не видно других отличий, то логично уделить внимание читаемости кода, не?
                    • 0
                      Вопрос в том, зачем нам вообще знать напрямую о наличии атрибутов модели вне модели. Такая необходимость — четкий признак применения антипаттерна anemic models.
                      • +2
                        Не все параметры, обрабатываемые в контроллере, являются полями модели.
                        • 0
                          В контроллере вообще не должны обрабатываться поля модели.

                          Касаемо же request-объекта — если нужно что-то сложнее, чем присвоение значения по умолчанию, это признак протекания бизнес-логики в контроллер.
                          • 0
                            Поля модели не должны, а поля пришедшие от пользователя должны. Нужно что-то сложнее. Конкретно, в зависимости от выбора пользователя выбрать какую модель дернуть и какой метод из контроллера. Соответственно, выбор пользователя тоже нужно проверить.
                            • 0
                              Какой дернуть метод — это уровень роутера.

                              Какую модель — мм, не могу придумать случая, который не сводится к передаче поля запроса в фабрику. Если вдруг что-то будет зависеть от наличия поля — мм, окей, это будет if ($request->has('foo')), куда тут всунуть оператор ??, все равно непонятно.
                              • 0
                                Какой дернуть метод — это уровень роутера.

                                Я про метод модели.
                                Если вдруг что-то будет зависеть от наличия поля — мм, окей, это будет if ($request->has('foo')), куда тут всунуть оператор ??, все равно непонятно.

                                Входящее поле не обязано быть таким простым, это может быть JSON, XML, массив и т.д. Его нужно будет распарсить в контроллере, выяснить нужные детали, а потом может и модель вызывать не придется, какой-нибудь редирект получится. И ваш пример с «has» ничем по сути не отличается от isset, вы ведь говорили именно о том, что не нужно проверять наличие существования полей в контроллере.
                                • 0
                                  Я говорил именно про isset, намекая на две плохие практики — непосредственную работу с superglobals и anemic models. Касаемо JSON etc — _иногда_ это действительно надо, когда есть какой-то особый случай и такая необходимость возникает в конкретном методе 1 раз. В общем же случае — это признак «толстого контроллера» и звоночек, говорящий о необходимости декомпозиции: JSON принципиально ничем не отличается от form-urlencoded, и точно так же может преобразовываться в request и подлежать маршрутизации.
                                  • 0
                                    Я говорил именно про isset, намекая на две плохие практики — непосредственную работу с superglobals и anemic models.

                                    Т.е. вы говорили про три несвязанные вещи, про isset, про superglobals и про anemic model. И почему-то это должно было оказаться аргументом против isset.
                                    В общем же случае — это признак «толстого контроллера» и звоночек, говорящий о необходимости декомпозиции: JSON принципиально ничем не отличается от form-urlencoded, и точно так же может преобразовываться в request и подлежать маршрутизации.

                                    В общем случае, нет никаких звоночков, задача контроллера и есть связь между пользователем (запросом) и моделью (бизнес-логикой). А толстый контроллер — это о прямом изменении модели в контроллере. А у нас речь об обработке сложных входящих пользовательских данных, не связанных с моделью.
                                    Конечно, все может преобразовываться в request, только тогда мы придем к другому антипаттерну GodObject.
                                    • 0
                                      Частое использование isset в контроллере обычно сочетается с superglobals и anemic models. Вот и всё, что имелось ввиду :)

                                      God Object не получится, если не пихать все в одну кучу. Из request-а может делаться FormRequest, из него Criteria и т.д.
                                      • 0
                                        Частое использование isset в контроллере обычно сочетается с superglobals и anemic models. Вот и всё, что имелось ввиду :)

                                        Наконец-то выяснили, что вы имеете в виду. Писали вы совершенно другое.
                                        Если приходится часто писать isset, это что-то не то с архитектурой.

                                        Не все задачи есть сайты. Не все придерживаются MVC. Не каждое MVC такое, как у вас. Любые внешние данные это потенциальный isset. А также,
                                        God Object не получится, если не пихать все в одну кучу. Из request-а может делаться FormRequest, из него Criteria и т.д.

                                        Ага, а кто должен эти объекты производить?
                              • 0
                                это уровень роутера

                                Есть даже такой уровень? Мне казалось это все уровень фронт-контроллера.
                                • 0
                                  Фронт-контроллер — это понятие довольно абстрактное. Много что туда относят: единая точка входа, подключение composer autoloader, инициализация DI, сборка реквеста, подключение какой-нибудь дебаг-консоли в отладочном режиме… Я предпочитаю не использовать этот термин вообще, он слишком широк.
                                  • 0
                                    ну как, подключение composer autoload я лично считаю процессом бутстрапинга, и это еще не фронт контроллер. Фронт контроллер в моем понимании это вот такой вот простой интерфейс (будем уже писать в терминах PHP7):

                                    interface Controller {
                                        public function handle(Request $request): Response;
                                    }
                                    


                                    а уж что там внутри особо разницы не имеет, маршрутизаторы ли там, иссеты и ифы со свитчами…
                                    • 0
                                      В том-то и проблема с этим термином. Авторы Symfony, например, считают иначе:

                                      symfony.com/doc/current/cookbook/configuration/front_controllers_and_kernel.html#the-front-controller
  • +1
    Есть уже гид как старые extensions переписывать чтобы в PHP7 работало? Типа XSLCache того же.
  • 0
    Со всем согласен, но BaseException — это какой-то отвратительный костыль
    • +3
      Вынужденный. Слишком уж много людей ленятся наследоваться от \Exception и отлавливать именно свои исключения :(
      • 0
        Можно было бы отнаследоваться от ErrorException. Его в 99% случаев используют по делу.

        А код, смешивающий «свои» обработчики исключений с «олдскульной» обработкой engine errors, все равно ж сломается, не?
        • 0
          ErrorException extends Exception {
          

          Документация

          Тут скорее полезно именно добавление абстрактного \BaseException, после которого уже не будет возникать подобных случаев

          BaseException (abstract)
           +- EngineException
           +- ParseException
           +- Exception
               +- ErrorException
               +- RuntimeException
                   +- ...
               +- ...
          
          • 0
            А как введение еще одного базового класса поможет остаться работоспособным коду вида

            $f = fopen(...);
            if (!$f) {
            $error = error_get_last();
            // handle error…
            }

            ?

            Никак же, улетит исключение еще из fopen.

            Другое дело, что, наверное, с этой точки зрения необработанное исключение лучше, чем catch чего-то «не того». Но кода, обрабатывающего php-ошибки «по-старинке», не заворачивающего их через set_error_handler, и при этом в принципе использующего исключения, я вообще не встречал на практике. Так что тут лечится какой-то невообразимый сферический говнокод в вакууме, а нормально написанный код, обрабатывающий ErrorException, придется переписывать.
  • +2
    Да, php был и остается живее всех живых, если еще после выхода PHP 7 проекты с веток 5.4 — 5.6 удастся почти безболезненно и быстро перевести, то цены не будет. В случае с python конечно грустно получилось, люди так до конца все и не перешли на 3 версию из за медленного перехода пакетов и библиотек.
    • –6
      Посмотрите на коммент ниже от Arilas

      У PHP нет будущего. Принятие рфц, в основном базируется на совместимости, и не способно, исправить архитектурные провалы языка. Дело становится совсем худо, если посмотреть с каким трудом принимаются необходимые для развития языка рфц. Для того чтоб принялись рфц, авторам приходится их извращать до неузнаваемости, а после внедрения подобных фич, приходишь ко мнению, что лучшеб вообще не делали.

      В общем, не вырастит этот язык до энтерпрайза, особенно с нынешними конкурентами. Нет, нет, писать большие проекты можно, но это как в м$ пейнте рисовать анимацию.
      • +1
        Да бросьте. Да у php есть свои недостатки в тех же стандартах, именование функций, но все это постепенно исправляется и улучшается от версии к версии. Свои задачи он прекрасно решает, а это низкий порок вхождения в язык, дешевизна разработки для бизнеса, просто огромное комьюнити которым не каждый язык может похвалиться. Да бывает что встречается плохой код, но это не от языка зависит, а от того, кто на нем пишет.
        • –3
          <irony> Простите был не прав, рфц принимаются на ура и не доводят контрибютеров до желания уйти! Реализация примесей — безупречна. А ооп, джависты обзавидуются! А скорость, еслиб не си, всё писали бы на php!!! синтаксис — эталон, который стоит в университетах подавать как учебный материал. </irony>

          С точки зрения бизнеса у php есть право на жизнь, пока… Но сегодня написать «hello world» на play2, проще чем на symfony2. И не надо говорить про порок вхождения, я не о домашних сайтах на коленке.
          • +2
            «Порок вхождения»! Боже… это шедевр!
            • 0
              Рад, что и вам понравилось, но лавры не мои.
          • 0
            контрибютеров до желания уйти

            Вы про драму со статическим тайп хинтингом?
            • –2
              В целом да, но ведь это уже не первый громкий уход.
  • +2
    Я уже даже доклад про «особенности» PHP 7 делал, очень много кривых и недоделанных возможностей:
    1. Нет nullable, если что-то может вернуть null, тип указать нельзя (привет find по id)
    2. declare(strict_types=1); — вот этот вот костыль просто говорит, что входить может int, выйдет string (оно если что отконвертирует), без него Type Hints — это конвертеры типов (написано string, передает объект, оно дергает __toString()).

    Несколько примеров, и комменты снизу кода:
    class Foo {
        public static function test() : self {
            return new Foo;
        }
    }
    class Bar extends Foo {
        public static function test() : parent {
            return new Bar;
        }
        public function action() : int {
            return 5;
        }
    }
    

    Как вам parent?
    namespace Bar;
    interface Foo {}
    interface FooManager {
    	public function bind():Foo;
    }
    class FooImpl implements Foo {}
    class FooManagerImpl implements FooManager {
    	public function bind():FooImpl {
    		return new FooImpl();
    	}
    }
    //Fatal error: Declaration of Bar\FooManagerImpl::bind() must be compatible with Bar\FooManager::bind(): Bar\Foo in *** on line 14
    

    Нет переопределения return'а, в других языках есть
    function foo(string $a):bool {
        return $a + 1;
    }
    var_dump(foo(5));//bool(true)
    

    А почему бы и нет?
    declare(strict_types=1);
    function foo($a):float {
        return $a + 1;
    }
    var_dump(foo("asdf5"));//float(1)
    

    Тоже нормально
    declare(strict_types=1);
    function foobar(float $abc): int {
        return ceil($abc + 1);
    }
    foobar(123.0);
    

    А тут ошибка, ceil возвращает float (хотя строку в предыдущем примере отконвертировало)
    • 0
      В любом языке можно найти способ выстрелить себе в ногу
    • 0
      strict_types — это вынужденный костыль для BC. Отсутствие nullable return types — да, серьезный косяк. С parent странно, конечно; видимо, из-за проблем с производительностью не проверяется цепочка наследования; хотя я бы тут использовал интерфейс. С floor/ceil/round проблема в том, что на 32-битных системах невозможно в общем случае сконвертировать в int без потерь, полагаю.
    • 0
      Ну слушайте, чего вы хотите от альфы-то? Про такие вещи пишите в bugs.php.net, сделаете язык лучше.
      • +1
        Это не баги, это то, что вошло в PHP 7 и не будет изменено(новые RFC не принимаются в PHP 7).
        1. По поводу Nullable — уже есть RFC, который висит очень давно, и даже не рассматривался (хотя ссылки на него есть в Return Type Hints)
        2. По поводу переопределения типа возврата, в изначальном RFC по Return Type Hints это было, и даже изначально его таким приняли, потом вспыло про BC и про перекрестную имплементацию, в итоге убрали.
        3. По поводу Scalar Type Hints, изначальный RFC был сделан очень интересно, там даже было оптимизировано выделение памяти для типов (по примеру статического анализатора типов в strict mode у hack)
        4. По поводу нового оператора ??, зачем-то изобрели новый костыль, могли бы заюзать то, как это допустим в том же js: var name = obj.name || «Name»
        5. По поводу нового оператора сравнения <=>, я не совсем понял зачем он (зачем сравнивать массивы и объекты?) а для чисел получать результат сравнения в виде -1 либо 0 (если равны) либо 1 (если больше) тоже не совсем понятно зачем.

        Вообще на PHP 7 я возлагал большие надежды, сейчас смотрю на HACK от HHVM, там есть очень много плюшек и нормальные Type Hint's(даже для массивов, Векторов, Мап, Callable и т.д.). А их статический анализатор — это просто сказка, он находит 90% багов прямо во время сохранения файлов.
        • +1
          4. По поводу нового оператора ??, зачем-то изобрели новый костыль, могли бы заюзать то, как это допустим в том же js: var name = obj.name || «Name»
          Во-первых, зачем использовать то, как в JS, если это не JS?

          Во-вторых, $var || $smth имеет тип возвращаемый тип bool, менять это — ломать обратную совместимость.

          В-третьих, «||» в JS и «??» в PHP ни разу не эквивалентны. $obj->name()['abc'] ?? $smth сработает в PHP, но не свалится в JS, если $obj не определён.
          • 0
            $obj->name()['abc'] ?? $smth

            Оно разве отловит Exception о том, что мы пытаемся вызвать метод у null? Это Recoverable Exception, но все же в определении ?? не указано, что он Exception'ы отлавливает, если бы он так себя вел, то помойму использовать его вообще нельзя (если нужно наружу выкинуть Exception, то что тогда?)

            Проверил:
            <?php
            $a = $obj->name()['abs'] ?? 'asfd';
            var_dump($a);
            

            Вывод:
            Fatal error: Call to a member function name() on null in *** on line 2
            

            То-есть он аналогичен js по поведению:
            var a = obj.some() || 5
            

            Uncaught ReferenceError: obj is not defined
            


            Большинство php разработчиков пишет часто на js, сейчас такая специфика, очень много client-side кода, и разное поведение одного и того же функционала часто приводит к банальным ошибкам (в большом проекте всегда есть коммит, который фиксит strpos с === либо сравнение с -1 вместо false во многих случаях)
            • 0
              Да, действительно, тут я сглупил. isset поведёт себя так же. Скорее надо проверять что-то вроде

              $obj->name->value ?? 'asdf';
              • 0
                Да, в данном случае сработало, но помойму это костыль, сразу через два null'а идти, напрямую дергать поля, а не методы, если у кого-то такое будет в коде — нужно по рукам сразу же :)
                • 0
                  Во всех языках это встречается. В JS — вообще постоянно.
    • 0
      Нет nullable

      Ну тут да, это проблема. К сожалению в момент накала страстей, когда принимали статический тайп хинтинг, разработчица которая ввела эти RFC просто скипнула, и из всех ее RFC (в числе которых был и нуллабл) сделали по сути только тайп хинтинг для скаляров. Думаю к версии 7.1 сделают.

      оно если что отконвертирует

      при strict_types=1 оно кинет ошибку, если вы что-то не то куда-то передали.

      Как вам parent?

      уже обсуждалось как-то. Не сказать что это частый кейс, но да, не приятно. Хотя в вашем примере пусть оно уж лучше Foo возвращает.
      • 0
        А почему бы и нет?

        А что в этом примере плохого? Вы передали значение, которое нельзя без потери данных привести к флоту. В результате там 0.

        А если так:
        declare(strict_types=1);
        function foo(float $a):float {
            return $a + 1;
        }
        
        var_dump(foo("asdf5"));
        //Fatal error: Argument 1 passed to foo() must be of the type float, string given
        


        или так:

        declare(strict_types=1);
        function foo(float $a):float {
            return $a + 1;
        }
        
        var_dump(foo("asdf5"));
        //Fatal error: Argument 1 passed to foo() must be of the type float, string given
        // к слову раньше там писало про catchable error....
        

        • 0
          Если сравнить strict types с тем же HACK, то он обозначает, что в файле должны быть указаны все типы, для всех входящих/выходящих переменных, в PHP 7 strict_types просто отключает некоторые конвертирования типов (то-есть если указан int, а передается string, он не будет строку в int перегонять).

          Я знаю как сделать чтобы код, который я скинул работал, проблема в другом, что все равно RFC очень поверхностные. Что дает strict mode в HACK:
          1. Полный статический анализ кода, и типов каждой переменной (в hh_client можно узнать тип каждой переменной, указав номер строки и символа, также можно получить автодополнение в любой редактор для любой части кода)
          2. Из-за того, что типы известны, есть экономия памяти, потому что не нужно делать структуру как в PHP для хранения значения
          3. Исходя из пункта выше, JIT некоторые части кода после прогрева переводит в нативный код (если помните HPHPc, то он компилил тогда весь проект, здесь, только если может, и может отдельные части)

          Что дает strict_types в PHP 7:
          1. Дополнительные проверки в runtime для того, чтобы проверить, что переменная действительно нужного типа
          2. Нельзя статически анализировать(нет полной картины типов, из-за отсутствия Nullable)
          • +1
            Исходя из пункта выше, JIT некоторые части кода после прогрева переводит в нативный код


            JIT все и всегда переводит в нативный код как бэ, просто со временем оптимизирующий компилятор делает все еще лучше, а профит в производительности достигается за счет того что скаляры не оборачиваются в обертку, то есть за счет анбоксинга. Посмотрите на JS, там вообще все типы динамические и оптимизации носят спекулятивный характер. То есть оптимизатор на основе статистики просто говорит что 99% что там инт, если вдруг чего откатывайся.

            В PHP7 нет JIT, а к 8-ой версии в принципе не проблема это сделать. Ну и да, судя по тому что от билда к билду поведение того же тайп хинтинга меняется, можно судить что это еще не стабильная реализация. И да, если вы считаете что-то багом, надо бежать в багтрекер а не пилить RFC.
  • 0
    За все время использования PHP в своих проектах, так и не придумал задачи с использованием генераторов… Пользуется, кто ими и для какой цели?
    • –1
      Как альтернатива lock'ам в cron job'ах, из PDOStatement каждый раз выбираем только одну вещь(ей сразу ставим статус, чтобы она не подходила под условия выборки), и если запрос достаточно большой, то это дает профит(несколько cron job одновременно в несколько процессов могут спокойно несколько часов работать, что дает хороший профит).
    • 0
      Ну самая стандартная задача генерировать id внутри транзакции еще какой-то дополнительный.
      Lazy evaluation/Stream function типа чтения из файлов и т.д. Ну вообщем-то не сказать, чтоб обширное поле деятельности было, но вполне себе имеет право на жизнь.
    • +1
      Есть хорошая статья на эту тему: Генераторы в действии
    • +2
      Например, если нам нужно перевести массив в вид [userId => user]. Преимущество — не расходуется память на лишнюю переменную.

      К тому же, разница на лицо

      function users()
      {
          foreach($users as $user)
              yield $user['id'] => $user;
      }
      

      function users()
      {
          $_users = [];
      
          foreach($users as $user)
              $_users[$user['id']] = $user;
      
          return $_users;
      }
      
    • 0
      Вы посмотрите языки, где они есть давно, это лучший способ их понят. Мне вспоминается хорошая статья Вани Сагалаева, где он рассказывал как он применил генераторы для конкретной задачи (язык там — «Пайтон», но кажется это не мешает восприятию): softwaremaniacs.org/blog/2010/09/18/ijson
      • 0
        А вот я сейчас прямо пишу статью, где парсер ijson на Rust реализую, и там будет как раз очень показательный пример полезности `yield`. Завтра, надеюсь, допишу.
    • 0
      Самый распространенный пример использования генераторов — реализация xrange для экономии памяти на больших размерах массивов.

      А еще есть такие клевые штуки как корутины.
      • 0
        Самый распространенный пример использования генераторов — реализация xrange для экономии памяти на больших размерах массивов.
        xrange с лёгкостью заменяется циклом. Это где же это самый распространённый пример?
        • +1
          Да как бы все на что-то слегкостью заменяется. Генераторы это ни что иное как сахар для итераторов. В контексте питона удобно, в контексте PHP единственный кейс когда мне пригодились генераторы — корутины.

          А ну и еще стрим-парсеры удобно делать.
          • 0
            Ну, короткого синтаксиса (генераторный выражений из «Пайтона») мне в ПХП не хватает, но в основном — годно. Для интереса запилил пайтонячий itertools на генераторах ПХП, получилось вполне съедобно.
    • 0
      Как правило это длинные задачи, когда процесс работает больше секунд. Например работа в режиме демона с сокетами, или парсинг, обсчёт большого количества данных, постороение каких нибудь кайнтеров или коэфициентов.
  • +2
    Заключение особо порадовало :) PHP — уже велик! (ударение поставьте сами :))
    • +1
      С ударениями там 4 варианта)
  • 0
    Круто! — уходит функция at(), которую я вечно пишу где-то

    function at($array, $index, $default)
    {
        return isset($array[$index]) ? $array[$index] : $default;
    }
    
    ...
    
    $name = at($_POST, 'name', '');
    // Стало
    $name = $_POST['name'] ?? '';
    
    • –2
      Давно уже можно так:
      <?php
      $a = $_POST['a'] ?: 'asdf';
      var_dump($a);
      

      И без всякого ??
      • 0
        Надо будет проверить, насколько работает «Элвис». Он в PHP относительно недавно, а т.к. я не профессионал, за этим не слежу.
        1) Как я понял, он работает по вычислимости в true, т.е. дока скрывает, что будет, когда такой переменной вообще нет.
        2) По той же причине для строки '0', скорее всего, вернёт значение по умолчанию.
      • 0
        $a ?: $b это аналог $a? $a: $b, а $a ?? $b — isset($a)? $a: $b

        совершенно разные вещи.

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