Это вторая часть нашей минисерии статей «Чего ждать от 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;
}
При каждой итерации будет выводиться:
- «Hello»
- " "
- «World!»
- «Goodbye»
- " "
- «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? Как вы относитесь к нововведениям? Есть ли что-то, с чем не согласны? Когда вы планируете перейти на новую версию? Напишите свои мысли по этому поводу в комментариях.
Комментарии (88)
andrewnester
18.05.2015 16:08+6лично очень жду именнованные параметры в методах/функциях, надеялся, что, быть может, придут к консенсусу до PHP 7, но к сожалению нет :(
mark_ablov
18.05.2015 16:15+5??
джвадесять лет ждал :)maximw
18.05.2015 16:38-5По-мне так просто сахарок. Ничего принципиально нового не вносит. Тренарник + isset() или empty() вполне себе годный для этих целей. Разве что цепочки не так красиво делаются, но цепочки это уже совсем экзотика.
AHDPEu
18.05.2015 16:54+1Для красоты всегда хватало
function is(){ foreach(func_get_args() as $v){ if(!is_null($v)){ return $v; } } } $value = is(null,null,false,true,1);
bolk
20.05.2015 09:24Дело в isset, а не в проверке на null.
AHDPEu
09.06.2015 11:55function 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']));
Работает как issetbolk
09.06.2015 12:01+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)
AHDPEu
09.06.2015 14:52У меня 5.4.32 версия под винду. Уведомление о несуществующим индексе не выскакивает. Разбираться, почему так происходит, совсем не хочется.
Проверил на убунте, так же версия из 5.4 ветки, уведомление есть.
Печаль, что тут сказать. А счастье было близко.iGusev Автор
09.06.2015 14:59Разбираться, почему так происходит, совсем не хочется.
Ответ содержится прямо в вопросе:
версия под винду
Используйте идентичное окружение и будет вам счастье.AHDPEu
09.06.2015 15:26Используйте идентичное окружение и будет вам счастье.
Так исторически сложилось — сервера с виндой, Internet Explorer, бюрократия…
А убунта личная.REZ1DENT3
11.06.2015 10:07<?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 значениеAHDPEu
11.06.2015 14:04Если принять тот факт, что первый параметр это «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);
Fesor
11.06.2015 14:15Чуть чуть 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);
AHDPEu
11.06.2015 14:28для 5.6 я так же описывал вариант, несколькими комментариями ниже.
Нужно добавить передачу параметров по ссылке. Иначе будут выскакивать уведомление о несуществующем индексе.
function is($default = null, &...$args) { ... }
Магия…
bolk
09.06.2015 15:07+1А вы запустите мой-то пример вместо своего. У вас там первый и единственный параметр по ссылке передаётся, а вы именно туда отсутствующий подставили, вот и вся разгадка.
AHDPEu
09.06.2015 15:23Вы абсолютно правы. Получилось повторить ошибку. Можно решить эту проблему с кучей входных параметров по ссылкам, с дефолтным значением null. Не слишком элегантно, но можно:
function is(&$args1=null, &$args2=null, &$args3=null, &$args4=null, ...) {
Ещё вариант для версии 5.6
function is(&...$args) {
mark_ablov
18.05.2015 17:23+7Да это-то понятно, но тем не менее писать
$blablabla ?? $default ?? 'None'
приятнее чем
isset($blablabla) ? $blablabla : (isset($default) ? $default : 'None')
symbix
18.05.2015 19:45-10Если приходится часто писать isset, это что-то не то с архитектурой.
mark_ablov
18.05.2015 19:51+1Ну как сказать, внешние данные же принимать как-то нужно.
В том числе и опциональные. И не всегда можно обойтись указанием $default в $request->param().
+ данные моделей тоже могут быть необязательными.symbix
18.05.2015 21:39-5$request->get('key', 'default')
Для «не всегда» — проверка на value lambda внутри обработчика default (как в Laravel):
$request->get('key', function() {
return result_of_some_computations;
});mark_ablov
18.05.2015 21:43+3Да, признаю, куда удобнее лепить многословное php-замыкание (небось еще и замкнуть внешние переменные придётся через use), чем написать простой one-liner.
symbix
18.05.2015 22:48-4Удобнее не нарушать инкапсуляцию. Количество кода — странный аргумент, так можно и переменные начать одной буквой называть.
mark_ablov
19.05.2015 04:16+4Каким образом isset() нарушает принципы инкапсуляции?
Не спорю, что красивая архитектура зачастую влечёт за собой большее число абстракций и больше кода, но в данном случае я в упор не вижу, чем пример с замыканием лучше использования isset().
А раз не видно других отличий, то логично уделить внимание читаемости кода, не?symbix
19.05.2015 16:53Вопрос в том, зачем нам вообще знать напрямую о наличии атрибутов модели вне модели. Такая необходимость — четкий признак применения антипаттерна anemic models.
sferrka
20.05.2015 09:26+2Не все параметры, обрабатываемые в контроллере, являются полями модели.
symbix
20.05.2015 18:15В контроллере вообще не должны обрабатываться поля модели.
Касаемо же request-объекта — если нужно что-то сложнее, чем присвоение значения по умолчанию, это признак протекания бизнес-логики в контроллер.sferrka
20.05.2015 19:08Поля модели не должны, а поля пришедшие от пользователя должны. Нужно что-то сложнее. Конкретно, в зависимости от выбора пользователя выбрать какую модель дернуть и какой метод из контроллера. Соответственно, выбор пользователя тоже нужно проверить.
symbix
20.05.2015 19:44Какой дернуть метод — это уровень роутера.
Какую модель — мм, не могу придумать случая, который не сводится к передаче поля запроса в фабрику. Если вдруг что-то будет зависеть от наличия поля — мм, окей, это будет if ($request->has('foo')), куда тут всунуть оператор ??, все равно непонятно.sferrka
20.05.2015 20:05Какой дернуть метод — это уровень роутера.
Я про метод модели.
Если вдруг что-то будет зависеть от наличия поля — мм, окей, это будет if ($request->has('foo')), куда тут всунуть оператор ??, все равно непонятно.
Входящее поле не обязано быть таким простым, это может быть JSON, XML, массив и т.д. Его нужно будет распарсить в контроллере, выяснить нужные детали, а потом может и модель вызывать не придется, какой-нибудь редирект получится. И ваш пример с «has» ничем по сути не отличается от isset, вы ведь говорили именно о том, что не нужно проверять наличие существования полей в контроллере.symbix
20.05.2015 20:47Я говорил именно про isset, намекая на две плохие практики — непосредственную работу с superglobals и anemic models. Касаемо JSON etc — _иногда_ это действительно надо, когда есть какой-то особый случай и такая необходимость возникает в конкретном методе 1 раз. В общем же случае — это признак «толстого контроллера» и звоночек, говорящий о необходимости декомпозиции: JSON принципиально ничем не отличается от form-urlencoded, и точно так же может преобразовываться в request и подлежать маршрутизации.
Fesor
20.05.2015 23:27это уровень роутера
Есть даже такой уровень? Мне казалось это все уровень фронт-контроллера.symbix
21.05.2015 00:34Фронт-контроллер — это понятие довольно абстрактное. Много что туда относят: единая точка входа, подключение composer autoloader, инициализация DI, сборка реквеста, подключение какой-нибудь дебаг-консоли в отладочном режиме… Я предпочитаю не использовать этот термин вообще, он слишком широк.
Fesor
21.05.2015 00:44ну как, подключение composer autoload я лично считаю процессом бутстрапинга, и это еще не фронт контроллер. Фронт контроллер в моем понимании это вот такой вот простой интерфейс (будем уже писать в терминах PHP7):
interface Controller { public function handle(Request $request): Response; }
а уж что там внутри особо разницы не имеет, маршрутизаторы ли там, иссеты и ифы со свитчами…symbix
21.05.2015 02:10В том-то и проблема с этим термином. Авторы Symfony, например, считают иначе:
symfony.com/doc/current/cookbook/configuration/front_controllers_and_kernel.html#the-front-controller
KlonD90
18.05.2015 21:26+1Есть уже гид как старые extensions переписывать чтобы в PHP7 работало? Типа XSLCache того же.
Blumfontein
18.05.2015 21:41Со всем согласен, но BaseException — это какой-то отвратительный костыль
iGusev Автор
18.05.2015 22:15+3Вынужденный. Слишком уж много людей ленятся наследоваться от \Exception и отлавливать именно свои исключения :(
symbix
19.05.2015 01:00Можно было бы отнаследоваться от ErrorException. Его в 99% случаев используют по делу.
А код, смешивающий «свои» обработчики исключений с «олдскульной» обработкой engine errors, все равно ж сломается, не?iGusev Автор
19.05.2015 01:07ErrorException extends Exception {
Документация
Тут скорее полезно именно добавление абстрактного\BaseException
, после которого уже не будет возникать подобных случаев
BaseException (abstract) +- EngineException +- ParseException +- Exception +- ErrorException +- RuntimeException +- ... +- ...
symbix
19.05.2015 02:13А как введение еще одного базового класса поможет остаться работоспособным коду вида
$f = fopen(...);
if (!$f) {
$error = error_get_last();
// handle error…
}
?
Никак же, улетит исключение еще из fopen.
Другое дело, что, наверное, с этой точки зрения необработанное исключение лучше, чем catch чего-то «не того». Но кода, обрабатывающего php-ошибки «по-старинке», не заворачивающего их через set_error_handler, и при этом в принципе использующего исключения, я вообще не встречал на практике. Так что тут лечится какой-то невообразимый сферический говнокод в вакууме, а нормально написанный код, обрабатывающий ErrorException, придется переписывать.
itcoder
18.05.2015 22:22+2Да, php был и остается живее всех живых, если еще после выхода PHP 7 проекты с веток 5.4 — 5.6 удастся почти безболезненно и быстро перевести, то цены не будет. В случае с python конечно грустно получилось, люди так до конца все и не перешли на 3 версию из за медленного перехода пакетов и библиотек.
Casus
19.05.2015 13:45-6Посмотрите на коммент ниже от Arilas…
У PHP нет будущего. Принятие рфц, в основном базируется на совместимости, и не способно, исправить архитектурные провалы языка. Дело становится совсем худо, если посмотреть с каким трудом принимаются необходимые для развития языка рфц. Для того чтоб принялись рфц, авторам приходится их извращать до неузнаваемости, а после внедрения подобных фич, приходишь ко мнению, что лучшеб вообще не делали.
В общем, не вырастит этот язык до энтерпрайза, особенно с нынешними конкурентами. Нет, нет, писать большие проекты можно, но это как в м$ пейнте рисовать анимацию.itcoder
19.05.2015 14:37+1Да бросьте. Да у php есть свои недостатки в тех же стандартах, именование функций, но все это постепенно исправляется и улучшается от версии к версии. Свои задачи он прекрасно решает, а это низкий порок вхождения в язык, дешевизна разработки для бизнеса, просто огромное комьюнити которым не каждый язык может похвалиться. Да бывает что встречается плохой код, но это не от языка зависит, а от того, кто на нем пишет.
Casus
19.05.2015 17:19-3<irony> Простите был не прав, рфц принимаются на ура и не доводят контрибютеров до желания уйти! Реализация примесей — безупречна. А ооп, джависты обзавидуются! А скорость, еслиб не си, всё писали бы на php!!! синтаксис — эталон, который стоит в университетах подавать как учебный материал. </irony>
С точки зрения бизнеса у php есть право на жизнь, пока… Но сегодня написать «hello world» на play2, проще чем на symfony2. И не надо говорить про порок вхождения, я не о домашних сайтах на коленке.
Arilas
18.05.2015 23:32+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 (хотя строку в предыдущем примере отконвертировало)symbix
19.05.2015 23:18strict_types — это вынужденный костыль для BC. Отсутствие nullable return types — да, серьезный косяк. С parent странно, конечно; видимо, из-за проблем с производительностью не проверяется цепочка наследования; хотя я бы тут использовал интерфейс. С floor/ceil/round проблема в том, что на 32-битных системах невозможно в общем случае сконвертировать в int без потерь, полагаю.
bolk
20.05.2015 09:33Ну слушайте, чего вы хотите от альфы-то? Про такие вещи пишите в bugs.php.net, сделаете язык лучше.
Arilas
20.05.2015 10:49+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% багов прямо во время сохранения файлов.bolk
20.05.2015 11:27+14. По поводу нового оператора ??, зачем-то изобрели новый костыль, могли бы заюзать то, как это допустим в том же js: var name = obj.name || «Name»
Во-первых, зачем использовать то, как в JS, если это не JS?
Во-вторых, $var || $smth имеет тип возвращаемый тип bool, менять это — ломать обратную совместимость.
В-третьих, «||» в JS и «??» в PHP ни разу не эквивалентны. $obj->name()['abc'] ?? $smth сработает в PHP, но не свалится в JS, если $obj не определён.Arilas
20.05.2015 11:44$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 во многих случаях)bolk
20.05.2015 12:15Да, действительно, тут я сглупил. isset поведёт себя так же. Скорее надо проверять что-то вроде
$obj->name->value ?? 'asdf';
Fesor
20.05.2015 10:46Нет nullable
Ну тут да, это проблема. К сожалению в момент накала страстей, когда принимали статический тайп хинтинг, разработчица которая ввела эти RFC просто скипнула, и из всех ее RFC (в числе которых был и нуллабл) сделали по сути только тайп хинтинг для скаляров. Думаю к версии 7.1 сделают.
оно если что отконвертирует
при strict_types=1 оно кинет ошибку, если вы что-то не то куда-то передали.
Как вам parent?
уже обсуждалось как-то. Не сказать что это частый кейс, но да, не приятно. Хотя в вашем примере пусть оно уж лучше Foo возвращает.Fesor
20.05.2015 11:00А почему бы и нет?
А что в этом примере плохого? Вы передали значение, которое нельзя без потери данных привести к флоту. В результате там 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....
Arilas
20.05.2015 11:18Если сравнить 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)Fesor
20.05.2015 13:46+1Исходя из пункта выше, JIT некоторые части кода после прогрева переводит в нативный код
JIT все и всегда переводит в нативный код как бэ, просто со временем оптимизирующий компилятор делает все еще лучше, а профит в производительности достигается за счет того что скаляры не оборачиваются в обертку, то есть за счет анбоксинга. Посмотрите на JS, там вообще все типы динамические и оптимизации носят спекулятивный характер. То есть оптимизатор на основе статистики просто говорит что 99% что там инт, если вдруг чего откатывайся.
В PHP7 нет JIT, а к 8-ой версии в принципе не проблема это сделать. Ну и да, судя по тому что от билда к билду поведение того же тайп хинтинга меняется, можно судить что это еще не стабильная реализация. И да, если вы считаете что-то багом, надо бежать в багтрекер а не пилить RFC.
REZ1DENT3
18.05.2015 23:42За все время использования PHP в своих проектах, так и не придумал задачи с использованием генераторов… Пользуется, кто ими и для какой цели?
Arilas
18.05.2015 23:47-1Как альтернатива lock'ам в cron job'ах, из PDOStatement каждый раз выбираем только одну вещь(ей сразу ставим статус, чтобы она не подходила под условия выборки), и если запрос достаточно большой, то это дает профит(несколько cron job одновременно в несколько процессов могут спокойно несколько часов работать, что дает хороший профит).
KlonD90
19.05.2015 00:14Ну самая стандартная задача генерировать id внутри транзакции еще какой-то дополнительный.
Lazy evaluation/Stream function типа чтения из файлов и т.д. Ну вообщем-то не сказать, чтоб обширное поле деятельности было, но вполне себе имеет право на жизнь.
kwolfy
19.05.2015 10:30+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; }
bolk
20.05.2015 09:28Вы посмотрите языки, где они есть давно, это лучший способ их понят. Мне вспоминается хорошая статья Вани Сагалаева, где он рассказывал как он применил генераторы для конкретной задачи (язык там — «Пайтон», но кажется это не мешает восприятию): softwaremaniacs.org/blog/2010/09/18/ijson
isagalaev
21.05.2015 03:09А вот я сейчас прямо пишу статью, где парсер ijson на Rust реализую, и там будет как раз очень показательный пример полезности `yield`. Завтра, надеюсь, допишу.
Fesor
20.05.2015 10:43Самый распространенный пример использования генераторов — реализация xrange для экономии памяти на больших размерах массивов.
А еще есть такие клевые штуки как корутины.bolk
20.05.2015 11:33Самый распространенный пример использования генераторов — реализация xrange для экономии памяти на больших размерах массивов.
xrange с лёгкостью заменяется циклом. Это где же это самый распространённый пример?Fesor
20.05.2015 11:46+1Да как бы все на что-то слегкостью заменяется. Генераторы это ни что иное как сахар для итераторов. В контексте питона удобно, в контексте PHP единственный кейс когда мне пригодились генераторы — корутины.
А ну и еще стрим-парсеры удобно делать.bolk
20.05.2015 15:53Ну, короткого синтаксиса (генераторный выражений из «Пайтона») мне в ПХП не хватает, но в основном — годно. Для интереса запилил пайтонячий itertools на генераторах ПХП, получилось вполне съедобно.
AmdY
11.06.2015 22:32Как правило это длинные задачи, когда процесс работает больше секунд. Например работа в режиме демона с сокетами, или парсинг, обсчёт большого количества данных, постороение каких нибудь кайнтеров или коэфициентов.
Mercury13
20.05.2015 12:32Круто! — уходит функция at(), которую я вечно пишу где-то
function at($array, $index, $default) { return isset($array[$index]) ? $array[$index] : $default; } ... $name = at($_POST, 'name', ''); // Стало $name = $_POST['name'] ?? '';
Arilas
20.05.2015 12:42-2Давно уже можно так:
<?php $a = $_POST['a'] ?: 'asdf'; var_dump($a);
И без всякого ??Mercury13
20.05.2015 13:00Надо будет проверить, насколько работает «Элвис». Он в PHP относительно недавно, а т.к. я не профессионал, за этим не слежу.
1) Как я понял, он работает по вычислимости в true, т.е. дока скрывает, что будет, когда такой переменной вообще нет.
2) По той же причине для строки '0', скорее всего, вернёт значение по умолчанию.
bolk
20.05.2015 15:54$a ?: $b это аналог $a? $a: $b, а $a ?? $b — isset($a)? $a: $b
совершенно разные вещи.
evnuh
Зачем перед именем класса всегда писать \?
iGusev Автор
Использование пространств имен: переход к глобальной функции/константе