Это не баги, это то, что вошло в 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% багов прямо во время сохранения файлов.
Как альтернатива lock'ам в cron job'ах, из PDOStatement каждый раз выбираем только одну вещь(ей сразу ставим статус, чтобы она не подходила под условия выборки), и если запрос достаточно большой, то это дает профит(несколько cron job одновременно в несколько процессов могут спокойно несколько часов работать, что дает хороший профит).
Я уже даже доклад про «особенности» 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 (хотя строку в предыдущем примере отконвертировало)
На операционках с длительным сроком поддержки часто возникает задача поставить какое-нибудь ПО новее, чем есть в репозитории, тогда и начинается веселье, которое приводит к проблемами стабильности, невозможности обновления (вручную модули перекомпилировали, и нужно отключить обновление этих компонентов, но от них зависит другое ПО, в итоге получается странная вещь)
На некоторых серверах у нас сейчас начали использовать Ubuntu Server 15.04, со времени беты, потому что там systemd, под который может кто угодно сделать сервис в несколько строк и версии ПО актуальные.
Я просто вспоминаю как приходилось извращаться в CentOS 6 для установки php 5.5, mariadb 10 и других. А еще раньше на CentOS 5 весело обновляли python. В общем дистрибутивами с долгой поддержкой можно пользоваться только с бубном (если уже прошло допустим 1,5 года)
Ну для новичков такие вот обертки над SQL'ом обязуют следовать каким-нибудь стандартам и не стрелять себе в ногу слишком часто.
Но как только задача начинает выходить из разряда обычных, эти PHQL, DQL, и т.д. начинают очень сильно портить все. К примеру взять PostgreSQL и его поле типа json, PHQL не даст работать с функциями PostgreSQL, и в то же время сама постгря не даст работать с полем как со строкой, в итоге получается что человек не может использовать все фичи того, что юзает.
К примеру есть запрос для постгри:
SELECT e.* FROM employee e, json_array_elements(e.roles) r WHERE r.value::text = :needRole
На DQL это будет:
никак(можно самому описать собственную функцию, описать ее конвертирование, но это займет over-много времени), такое сделать нельзя, только если указать поле типа text и вручную его из json и в json перегонять
WebRTC оно уже умеет? Или они до сих пор свой ORTC пишут уже три года?
А что насчет вот этого:
var node = document.querySelector('.some');
node.parentNode.innerHTML = '';
console.log(node);
Оно до сих пор очищает детей и аттрибуты у node? Или же как нормальные браузеры сохраняет так как ссылка осталась?
А что насчет console.log в js-коде, он так же стопает исполнение если случайно забыли убрать?
Хорошая статья, но вот у меня вопрос:
У меня косоглазие, и раньше я не мог смотреть фильмы в 3D, все они были такими, как будто я очки и не одевал. После одной болезни на глазах стал видеть фильмы в 3D(в любых очках).
Можно ли это объяснить?
Ну я могу сказать по каждому из пунктов:
1. RFC про Nullable зарегистрирован очень давно, но в обсуждении решили что эта вещь почему-то не нужна, и предложили выкидывать Exception'ы, хотя обещали подумать.
2. От этого отказались после первого варианта RFC где это было возможно, из-за того, что поведение не похоже на поведение входящих переменных (там тоже нет «уточнения»), и также падение производительности.
3. Также как и с пунктом 2.
Но вообще в Hack это есть, и они даже добавили статический анализатор типов для strict режима, который висит как демон, и hhvm просто обращается к нему: есть ошибки? нет, значит пусть приложение работает, если что в любой момент можно Exception выкинуть. И это дает реальный прирост производительности, потому что strict файлы отлично проверены как в Java. К примеру:
<?hh //strict
class User {
protected int $id;
public function getId():int {
return $this->id;
}
}
class UserManager {
public function getIdentifier(User $user):int {
return $user->getId();
}
}
Сразу выкинет ошибки:
1. protected int $id — не задается в конструкторе и не имеет значение по умолчанию, поэтому нужно пометить как nullable:
protected ?int $id;
2. метод getId — может возвращать null, нужно пометить что он nullable, то-есть:
public function getId():?int{}
3. Выход из getIdentifier() может иметь null, нужно пометить.
И он очень хорошо просматривает то, какие типы должны использоваться, он в strict режиме запрещает использовать обычные массивы, рекомендует использовать Map, Vector, etc в указанием типа. И большинство ошибок сразу же всплывают во время сохранения файла.
Return Type Hints реализованы ужасно, и их использовать в таком виде пагубно для кода. Основные вещи:
1. Нет Nullable при возврате:
public function find($id):User {
return null;
}
Приведет к Fatal Error (хотя в нормальных языках возврат null нормальное явление)
2. Нет «уточнения»
interface EntityInterface {}
interface EntiryRepositoryInterface {
public function find($id):EntityInterface;
}
class User implements EntityInterface {}
class UserRepository implements EntityRepositoryInterface {
public function find($id):User {
}
}
Это также Fatal Error
3. Переопределение класса и возврат this также не дает указать что мы возвращаем объект насследника, мы указываем тип предка, то-есть автодополнение будет считать что это предок.
В общем в текущем виде Return Type Hints вообще неюзабельны для классов, но могут нормально использоваться только для скаляров. Пара примеров еще: делал для доклада о HACK vs PHP 7
Эм, то-есть в K-Meleon скорость отрисовки интерфейса браузера должна привести к повышению скорости? А не кажется ли Вам, что сейчас основную нагрузку все же дают именно сайты, где важны:
1. Быстрый JS движок;
2. Поддержка стандартов (HTML5, CSS3, WebRTC, etc);
3. Сборщик мусора;
4. и т.д.
И на самом деле интерфейс любого браузера — это очень малозатратная составляющая в современном вебе, и увеличение потребляемой оперативной памяти для интерфейса к примеру на 100 мб — это не так уж и важно, ведь очень многие сайты в памяти разрастаются до нескольких сотен мегабайт даже при неактивности.
Очень давно, когда еще был такой браузер как Flock 2-й версии (который я когда-то тоже использовал), пользовался вашим браузером, мигрировал на него с Opera, Mozilla на тот момент казалась очень тяжелой, а K-Meleon был очень быстрым и мало потреблял ресурсов (на тот момент у меня было всего 256 метров оперативы, 2005-й год).
А откуда 75-я версия? Был же 1.5, и в вечной альфе 1.6.
По поводу первого, в JS вообще есть вещи как Node, NodeList, etc которые нельзя использовать ни как конструктор(попробуйте инстанцировать), ни как функцию. Но при этом все мы знаем что мы работаем с объектами инстанцированными от них. То-есть это какбы класс, но который нельзя инстанцировать. Но вообще есть еще так называемый Text, который насследуется от Node, и мы его можем спокойно инстанцировать.
Вообще я на собеседованиях люблю наблюдать как мыслит человек в задачке поинтереснее:
var A = function(){
this.items = [];
}
A.prototype = {
addItem: function(item){
this.items.push(item);
}
};
var B = extend(A, {
constructor: function(){
this.addItem(Math.random);
}
})
var elements = [
new B,
new B
];
И вопрос в том, что должно быть в extend, чтобы мы получили: items общую между всеми B и на отдельную для каждого инстанса.
RockMelt давно умер(официально), так же как и когда-то Flock. Comodo Dragon был создан чтобы быть подобным на Tor Browser, как известно Comodo известный Firewall, и они предложили безопасный браузер, но вроде он тоже умер.
Помню года 3 назад писал подобный require, но для анонимных define'ов парсил Stack Trace у Error и загрузка была без добавления тега script в head (eval).
Вообще задача интересная, но есть много подводных камней дальше(сборка в один пакет, paths, shim, export, и другие).
Что будет с любым ActiveRecord в случае если мы объект (к примеру User'а) ложим в Сессию. По логике связь с базой и вообще ORM прерывается, и после восстановления из базы у нас просто репрезентация данных, но(сразу дам пару костылей которые используют):
— К примеру нужно сделать правки в объекте восстановленном из сессии и мы их делаем, пытаемся вызвать save… и ошибка, нет связи.
1. Часто в Entity обратно получают подключение либо враппер через статические методы, превращая соединение в Signleton и блокируя возможность соединения с несколькими базами либо если хранится идентификатор это все равно не очень так как все равно нельзя явно это все переопределить.
2. Иногда с этой задачей справляются сами AuthenticationService либо SessionContainer, которые обратно при восстановлении инжектят нужные вещи внутрь той же Entity и мы получаем обратно работающий save. Но это очень костыльно, и не всегда можно точно определить откуда объект (к примеру в проекте у меня динамически увеличивается количество баз данных MongoDB под каждый «проект»).
— ActiveRecord обычно не хранят уже созданные Entity в каком-то UnitOfWork либо EntityPersister'е, поэтому очень часто при получении одного и той же записи мы получаем разные объекты, которые между собой не связаны, из-за этого возникают проблемы такие как: Мы обновляем текущего пользователя (полученного из запроса), но в сессии Юзер не обновился после привязки к менеджеру. Это решено достаточно хорошо в Data Mapper'ах где вместо оригинальной Entity часто отдает проксю, котора тянет данные не из реальной репрезентации а их общего хранилища оригиналов (__initializer__ в Doctrine который обращается к EntityPersister'у и берет оттуда данные). Что в данном случае у ActiveRecord:
1. Есть пару попыток реализации кеша этих самых Entity, но часто они сделаны не совсем гибко: отдается один и тот же объект, но есть проблема с случайным обновлением, которое еще не заперсистили, но мы уже везде его видим.
Времени мало, но таких «особенностей» можно описать очень много, я сам являюсь контрибьютором в Doctrine, и пишу собственную легковесную ORM совместимую с Doctrine но лишенную многих вещей влияющих на скорость.
Немного объяснений:
У вас есть ваша любимая ORM'ка (к примеру Doctrine), у которой есть AbstractRepository, который используется как репозиторий по умолчанию, и также вы от него можете насследоваться. У этого AbstractRepository есть метод find, который возвращает нам объект Entity. Представим, что все Entity реализуют интерфейс EntityInterface, тогда наш AbstractRepository будет иметь вид:
class AbstractRepository
{
public function find($id): EntityInterface
{
...
}
....
}
Вроде все правильно, find действительно возвращает EntityInterface, но если мы будем использовать его для определенной Entity, к примеру User, мы не сможем при наследовании переопределить тип возвращаемого значения как User, то-есть такой код приведет к Fatal Error:
class UserRepository extends AbstractRepository
{
public function find($id): User
{
...
}
....
}
То-есть нет сужения/расширения классов, возьмем к примеру Java:
public interface A {
public A create();
}
...
public class HelloWorld implements A
{
public static void main(String []args){
HelloWorld a = new HelloWorld();
a.create();
}
public HelloWorld create() {
return this;
}
}
Такой код является валидным, так как он не противоречит условию.
Return Types долго ждал, понравилось как было сделано в первой версии RFC, которую забраковали, исправили это поведение так:
interface A {
static function make(): A;
}
class B implements A {
static function make(): B { // must exactly match parent; this will error
return new B();
}
}
Вот из-за этого поведения, return_types не будет использоваться активно. К примеру в Hack можно указать тип возвращаемого значения как 'this', то в текущей реализации в PHP этого сделать будет нельзя. В итоге люди все равно будут дальше использовать обычный phpDoc: @ return $this для этих целей, и IDE будет работать правильно.
Если бы хотя бы добавили Генерики, было бы намного проще:
class AbstractRepository<T>
{
public function find($id): ?T
{
...
}
....
}
Так было бы намного удобнее это сделать (к примеру так сделано в Hack);
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. Нет nullable, если что-то может вернуть null, тип указать нельзя (привет find по id)
2. declare(strict_types=1); — вот этот вот костыль просто говорит, что входить может int, выйдет string (оно если что отконвертирует), без него Type Hints — это конвертеры типов (написано string, передает объект, оно дергает __toString()).
Несколько примеров, и комменты снизу кода:
Как вам parent?
Нет переопределения return'а, в других языках есть
А почему бы и нет?
Тоже нормально
А тут ошибка, ceil возвращает float (хотя строку в предыдущем примере отконвертировало)
На некоторых серверах у нас сейчас начали использовать Ubuntu Server 15.04, со времени беты, потому что там systemd, под который может кто угодно сделать сервис в несколько строк и версии ПО актуальные.
Я просто вспоминаю как приходилось извращаться в CentOS 6 для установки php 5.5, mariadb 10 и других. А еще раньше на CentOS 5 весело обновляли python. В общем дистрибутивами с долгой поддержкой можно пользоваться только с бубном (если уже прошло допустим 1,5 года)
Но как только задача начинает выходить из разряда обычных, эти PHQL, DQL, и т.д. начинают очень сильно портить все. К примеру взять PostgreSQL и его поле типа json, PHQL не даст работать с функциями PostgreSQL, и в то же время сама постгря не даст работать с полем как со строкой, в итоге получается что человек не может использовать все фичи того, что юзает.
К примеру есть запрос для постгри:
На DQL это будет:
никак(можно самому описать собственную функцию, описать ее конвертирование, но это займет over-много времени), такое сделать нельзя, только если указать поле типа text и вручную его из json и в json перегонять
А что насчет вот этого:
Оно до сих пор очищает детей и аттрибуты у node? Или же как нормальные браузеры сохраняет так как ссылка осталась?
А что насчет console.log в js-коде, он так же стопает исполнение если случайно забыли убрать?
У меня косоглазие, и раньше я не мог смотреть фильмы в 3D, все они были такими, как будто я очки и не одевал. После одной болезни на глазах стал видеть фильмы в 3D(в любых очках).
Можно ли это объяснить?
1. RFC про Nullable зарегистрирован очень давно, но в обсуждении решили что эта вещь почему-то не нужна, и предложили выкидывать Exception'ы, хотя обещали подумать.
2. От этого отказались после первого варианта RFC где это было возможно, из-за того, что поведение не похоже на поведение входящих переменных (там тоже нет «уточнения»), и также падение производительности.
3. Также как и с пунктом 2.
Но вообще в Hack это есть, и они даже добавили статический анализатор типов для strict режима, который висит как демон, и hhvm просто обращается к нему: есть ошибки? нет, значит пусть приложение работает, если что в любой момент можно Exception выкинуть. И это дает реальный прирост производительности, потому что strict файлы отлично проверены как в Java. К примеру:
Сразу выкинет ошибки:
1. protected int $id — не задается в конструкторе и не имеет значение по умолчанию, поэтому нужно пометить как nullable:
2. метод getId — может возвращать null, нужно пометить что он nullable, то-есть:
3. Выход из getIdentifier() может иметь null, нужно пометить.
И он очень хорошо просматривает то, какие типы должны использоваться, он в strict режиме запрещает использовать обычные массивы, рекомендует использовать Map, Vector, etc в указанием типа. И большинство ошибок сразу же всплывают во время сохранения файла.
1. Нет Nullable при возврате:
Приведет к Fatal Error (хотя в нормальных языках возврат null нормальное явление)
2. Нет «уточнения»
Это также Fatal Error
3. Переопределение класса и возврат this также не дает указать что мы возвращаем объект насследника, мы указываем тип предка, то-есть автодополнение будет считать что это предок.
В общем в текущем виде Return Type Hints вообще неюзабельны для классов, но могут нормально использоваться только для скаляров. Пара примеров еще: делал для доклада о HACK vs PHP 7
1. Быстрый JS движок;
2. Поддержка стандартов (HTML5, CSS3, WebRTC, etc);
3. Сборщик мусора;
4. и т.д.
И на самом деле интерфейс любого браузера — это очень малозатратная составляющая в современном вебе, и увеличение потребляемой оперативной памяти для интерфейса к примеру на 100 мб — это не так уж и важно, ведь очень многие сайты в памяти разрастаются до нескольких сотен мегабайт даже при неактивности.
А откуда 75-я версия? Был же 1.5, и в вечной альфе 1.6.
Вообще я на собеседованиях люблю наблюдать как мыслит человек в задачке поинтереснее:
И вопрос в том, что должно быть в extend, чтобы мы получили: items общую между всеми B и на отдельную для каждого инстанса.
Вообще задача интересная, но есть много подводных камней дальше(сборка в один пакет, paths, shim, export, и другие).
— К примеру нужно сделать правки в объекте восстановленном из сессии и мы их делаем, пытаемся вызвать save… и ошибка, нет связи.
1. Часто в Entity обратно получают подключение либо враппер через статические методы, превращая соединение в Signleton и блокируя возможность соединения с несколькими базами либо если хранится идентификатор это все равно не очень так как все равно нельзя явно это все переопределить.
2. Иногда с этой задачей справляются сами AuthenticationService либо SessionContainer, которые обратно при восстановлении инжектят нужные вещи внутрь той же Entity и мы получаем обратно работающий save. Но это очень костыльно, и не всегда можно точно определить откуда объект (к примеру в проекте у меня динамически увеличивается количество баз данных MongoDB под каждый «проект»).
— ActiveRecord обычно не хранят уже созданные Entity в каком-то UnitOfWork либо EntityPersister'е, поэтому очень часто при получении одного и той же записи мы получаем разные объекты, которые между собой не связаны, из-за этого возникают проблемы такие как: Мы обновляем текущего пользователя (полученного из запроса), но в сессии Юзер не обновился после привязки к менеджеру. Это решено достаточно хорошо в Data Mapper'ах где вместо оригинальной Entity часто отдает проксю, котора тянет данные не из реальной репрезентации а их общего хранилища оригиналов (__initializer__ в Doctrine который обращается к EntityPersister'у и берет оттуда данные). Что в данном случае у ActiveRecord:
1. Есть пару попыток реализации кеша этих самых Entity, но часто они сделаны не совсем гибко: отдается один и тот же объект, но есть проблема с случайным обновлением, которое еще не заперсистили, но мы уже везде его видим.
Времени мало, но таких «особенностей» можно описать очень много, я сам являюсь контрибьютором в Doctrine, и пишу собственную легковесную ORM совместимую с Doctrine но лишенную многих вещей влияющих на скорость.
У вас есть ваша любимая ORM'ка (к примеру Doctrine), у которой есть AbstractRepository, который используется как репозиторий по умолчанию, и также вы от него можете насследоваться. У этого AbstractRepository есть метод find, который возвращает нам объект Entity. Представим, что все Entity реализуют интерфейс EntityInterface, тогда наш AbstractRepository будет иметь вид:
Вроде все правильно, find действительно возвращает EntityInterface, но если мы будем использовать его для определенной Entity, к примеру User, мы не сможем при наследовании переопределить тип возвращаемого значения как User, то-есть такой код приведет к Fatal Error:
То-есть нет сужения/расширения классов, возьмем к примеру Java:
Такой код является валидным, так как он не противоречит условию.
Вот из-за этого поведения, return_types не будет использоваться активно. К примеру в Hack можно указать тип возвращаемого значения как 'this', то в текущей реализации в PHP этого сделать будет нельзя. В итоге люди все равно будут дальше использовать обычный phpDoc: @ return $this для этих целей, и IDE будет работать правильно.
Если бы хотя бы добавили Генерики, было бы намного проще:
Так было бы намного удобнее это сделать (к примеру так сделано в Hack);
Это PostgreSQL, она умеет