image На Хабре уже был перевод с обзором несколько месяцев назад, но недавно вышел первый релиз-кандидат PHP 7.1, а значит никаких существенных изменений больше не будет и можно сказать, какие точно изменения будут в релизе. Я решил немного оживить сухой “changelog” своим вольным переводом изменений, которые принесет нам новая минорная версия 7.х ветки.

Новая функциональность


Добавлен возвращаемый тип «void» (RFC)


Теперь функции и методы, которые не должны ничего возвращать, можно помечать возвращаемым типом void:

function someNethod(): void {
    // работает если return отсутсвует
    // работает с return;
    // не работает если return null;
    // не работает если return 123;
}

Возврат какого-то значения из метода/функции, который помечен как void, будет генерировать исключение уровня Fatal Error. Обратите внимание, что NULL значение не приравнивается к void (отсутствию значения), то есть возращать NULL нельзя.

Кстати, это не значит что $x = someNethod(); не вернет ничего. Как и прежде в $x будет значение NULL. Так же void нельзя использовать как тип к параметру.


function bar(void $foo) {}
// Выбросит: Fatal error: void cannot be used as a parameter type in....

Добавлен новый псевдо-тип: «iterable» (RFC)



function walkList(iterable $list): iterable {
    foreach ($list as $value) {
        yield $value[‘id’];
    }
}

Этот тип по сути объединяет примитивный тип array и интерфейс Traversable (а значит и его производные: Iterator, Generator, etc). Проблема возникла на почве того, что к примеру, foreach может работать с обоими типами, но функция с типом array не примет объект с интерфейсом Traversable и наоборот.

Так же в рамках этого RFC была добавлена новая функция is_iterable(), которая работает аналогично другим is_* функциям.

Появилась возможность разрешать null в типизированных и возвращаемых параметрах (Nullable RFC)



    function callMethod(?Bar $bar): ?Bar {}
    $this->callMethod($bar); // Работает
    $this->callMethod(null);   // Работает
    $this->callMethod();        // НЕ работает

Обратите внимание, что использование "?" и значение null по умолчанию не одно и тоже что


 function callMethod(int $bar = null) {}
$this->callMethod(1); // Работает
$this->callMethod(null); // Работает
$this->callMethod(); // Тоже работает

Причем добавление "?" оставляет поведение обратно совместимым

function callMethod(?Bar $bar = null) {}
 // Работает так же как и без “?”

Также важный момент по наследованию:


interface Fooable {
    function foo(int $i): ?Fooable;
}
interface StrictFooable extends Fooable {
    function foo(?int $i): Fooable; // valid
}

В наследнике можно делать «строже» возвращаемый тип (то есть запрещать nullable), а параметр наоборот расширять до nullable, НО не наоборот!

Добавлена возможность использовать отрицательное значение для смещения в строках (RFC)


echo $msg[-1]; // вернет последний символ
echo $msg{-3}; // Причем RFC явно рекомендует использовать способ $str{} так как $str[] может сбивать с толку И в будущем может быть объявлен как устаревшим.

Отрицательные значения так же стали разрешены в некоторых строковых функциях: strpos, stripos, substr_count, grapheme_strpos, grapheme_stripos, grapheme_extract, iconv_strpos, file_get_contents, mb_strimwidth, mb_ereg_search_setpos, mb_strpos, mb_stripos.

Везде это означает считать смещение с конца строки.

Разрешено использовать строковые ключи в конструкции list() (RFC)


Так же был добавлен короткий синтаксис для list (RFC).


["test" => $a, "name" => $b] = ["name" => "Hello", "test" => "World!"];
var_dump($a); // World!
var_dump($b); // Hello

Особенности:

  • нельзя использовать смешанный синтаксис (если указываем ключи — то указываем их везде, если нет, то используются обычные индексы 0, 1, 2… как обычно):

    
    // Parse error: syntax error, ...
    ["a" => $a, $b] = ["a" => 1, 2]
    

  • пустые элементы с ключами тоже же не разрешены:

    
    // Parse error: syntax error, ...
    list(,,,, "key" => $keyed) = $array;
    

  • если ключа в исходном массиве нет, то будет выброшено предупреждение Notice: Undefined index: name, а в переменной будет NULL

  • при использовании вложенной конструкции list способы можно комбинировать

    
    $points = [
        ["x" => 1, "y" => 2],
        ["x" => 2, "y" => 1]
    ];
    [["x" => $x1, "y" => $y1], ["x" => $x2, "y" => $y2]] = $points;
    

Конвертация callable выражений в замыкание (RFC)


Closure::fromCallable(callable $calback);

Вот наглядный пример применения:

class A {
public function getValidator(string $name = 'byDefault') {
    return Closure::fromCallable([$this, $name]);
}
private function byDefault(...$options) {
	echo "Private default with:".print_r($options, true);
}
public function __call ( string $name , array $args ) {
	echo "Call $name with:".print_r($args, true);
}
}

$a = new A();
$a->getValidator("test")(1,2,3);
// Call test with: Array ( [0] => 1 [1] => 2 [2] => 3 )
$a->getValidator()(‘p1’, ‘p2’);
// Private default with: Array ( [0] => ‘p1’, [1] => ‘p2’)
// Внимание Closure::fromCallable передает контекст ($this) в момент вызова внутрь замыкания, тем самым разрешая обращаться к приватным методам
// если оставить только return [$this, $name]; то 
$a->getValidator()(‘p1’, ‘p2’);
// вернет 
// Call byDefault with:Array ( [0] => p1 [1] => p2 )
// то есть вызовет только публичный метод и не будет иметь доступа к приватным методам объекта

Поддержка модификаторов видимости для констант класса (RFC)



class Token {
	// Константа без модификатора по умолчанию “public”
	const PUBLIC_CONST = 0;
 
        // Константы с различной областью видимости
        private const PRIVATE_CONST = 0;
        protected const PROTECTED_CONST = 0;
        public const PUBLIC_CONST_TWO = 0;
 
        // Весь список имеет одну область видимости
        private const FOO = 1, BAR = 2;
}

Ловить исключения можно объединяя несколько типов исключений в один блок (RFC)



try {
   echo "OK";
} catch (Exception | DomainException $e) {
   // ... обработка 2ух типов исключений сразу
} catch (TypeError $e) {
   // ...
}

Выбросы ошибок уровня E_NOTICE and E_WARNING при арифметических операциях над строками содержащие не валидные числа (RFC)



$numberOfApples = "10 apples" + "5 pears";
// Выбросит
// Notice: A non well formed numeric string encountered in example.php on line 3
// Notice: A non well formed numeric string encountered in example.php on line 3
$numberOfPears = 5 * "orange";
// Warning: A non-numeric string encountered in example.php on line 3

Это довольно важное изменение, которое теоритически может сломать обратную совместимость приложения если используются свои error handlers для перехвата предупреждений.

Причем есть интересная особенность: пробел в начале строк “ 5” + “ 3” — не даст ошибок. А вот “5 ” + “3 ” — пробел в конце уже даст выдаст предупреждения.

Для обхода последствий неявного преобразования и выброса предупреждений можно явно указывать “cast” в нужный тип: (int)“5 ” + (int)“3 ” или подавлять все принудительно @(“5 ” + “3 ”).

Другие изменения и обратные несовместимости


  • В связи с новыми типами, добавлены новые зарезервированные слова void, iterable, и код который содержит классы, интерфейсы, трейты с такими именами будет давать ошибку в 7.1

  • Поменяли поведение в php экстеншенах, которые продолжали выкидывать Fatal Error вместо генерации Error исключения (как текущее ядро 7.0), плюс ошибки уровня E_ERROR или E_RECOVERABLE_ERROR тоже стали выбрасывать исключения там, где возможно (понятное дело, что при нехватки памяти по прежнему скрипт необратимо падает (RFC)).

  • Изменилось поведение при вызове функций / методов без передачи обязательных аргументов. Теперь вместо привычного Warning предупреждения, будет выброшено исключение ArgumentCountError (наследует тип Error RFC):

    
    function foo($a) {
       var_dump($a);   // теперь исполнение сюда не дойдет и в $a не будет NULL
    }
    foo();
    // Fatal error: Uncaught ArgumentCountError: Too few arguments to function foo(), 0 passed in...
    

  • Следующие функции больше нельзя вызвать динамически через: $func(), call_user_func(), array_map() и тд:

    1. extract()
    2. compact()
    3. get_defined_vars()
    4. func_get_args()
    5. func_get_arg()
    6. func_num_args()
    7. parse_str() с одним аргументом
    8. mb_parse_str() с одним аргументом
    9. assert() больше нельзя использовать строку в качестве агрумента

  • Функции rand() и srand() теперь просто псевдонимы (alias) к функциям mt_rand() и mt_srand().
    Это в свою очередь затронет вывод таких функций:

    1. rand()
    2. shuffle()
    3. str_shuffle()
    4. array_rand()

  • Добавлена функция session_gc(). Теперь можно чистить старые сессии прямо из скриптов.

  • Добавлена функция session_create_id(), которая позволяет сгенерировать валидный автоматический id сесии без запуска новой сесии, который можно будет использовать в session_id() для старта сессии со сгенерированным ранее ID.

  • Ускорили генерацию ID сессии в 2+ раз, убрав хеширование и используя новую функцию из 7.0 php_random_bytes()

    Скорость до: Requests per second: 899.36 [#/sec]
    Скорость после: Requests per second: 2278.59 [#/sec]
    

  • Убрали неконсистентное поведение над переменной $this

    
    function foo($this) { // Fatal error: Cannot use $this as parameter
    }
    static $this; // Fatal error: Cannot use $this as static variable
    global $this; // Fatal error: Cannot use $this as global variable
    try {
      ...
    } catch (Exception $this) { // Fatal error: Cannot re-assign $this
    }
    foreach ($a as $this) { // Fatal error: Cannot re-assign $this
    }
    unset($this); // Fatal error: Cannot unset $this
    $a = "this";
    $$a = 42; // throw new Error("Cannot re-assign $this")
    function foo() {
        var_dump($this); // throws "Using $this when not in object context"
                         // php-7.0 emitted "Undefined variable: this" and printed NULL
    }
    foo();
    // и другие кейсы

  • Расширение mcrypt помечено как устаревшее и все mcrypt_* функции будут выкидывать E_DEPRECATED.

  • В curl расширение добавлена поддержка для HTTP/2 Server Push, так же были добавлены новый функции curl_multi_errno(), curl_share_errno(), curl_share_strerror().

  • Опция 'e' для функций mb_ereg_replace() и mb_eregi_replace() обьявлена устаревшей.

На этом мы пожалуй и остановимся, хотя там еще полно мелких изменений в основном в расширениях. А нам для холивара вполне хватит и этого списка. )

Итог


Лично моё мнение про данный минорный релиз: все очень органично вписалось, именно этого и не хватало в большинстве своем в новом PHP 7.0 и данные изменения лишь подчеркивают и усиливают особенности 7.х ветки.
Рекомендую дождаться 7.1.1 и можно обновляться без страха, что-то сломать (если вы конечно уже перешли на 7.0).

Данная статья не претендует на полное описание ВСЕХ изменений и я мог пропустить что-то важное, рекомендую все равно ознакомиться с первоисточниками:
» https://wiki.php.net/rfc#php_71
» https://github.com/php/php-src/blob/php-7.1.0RC1/UPGRADING

P.S. Примеры можно испытать самому в онлайн песочнице — 3v4l.org/#version=7.1.0RC1
Какую версию PHP вы преимущественно используете в своих проектах

Проголосовало 966 человек. Воздержалось 148 человек.

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

Поделиться с друзьями
-->

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


  1. Anexroid
    13.09.2016 15:38
    -1

    Что-то не совсем понял, как использовать void в параметрах функции.

    То есть?

    $x = 5;
    
    function foo(void $x) : int {
       return $x; 
    }
    
    

    Вернет 5? Или данный код некорректен?


    1. Drim
      13.09.2016 15:42
      +2

      Так же void нельзя использовать как тип к параметру.


    1. newmindcore
      13.09.2016 15:50
      +2

      Так же void нельзя использовать как тип к параметру.

      Данный код некорректен.


      1. Anexroid
        14.09.2016 12:25
        +1

        А, извиняюсь, неправильно прочитал.


  1. gro
    13.09.2016 16:33
    +1

    > Добавлен новый псевдо-тип: «iterable»

    Продолжаем потихонечку-потихонечнку двигаться вперёд.
    В 8.1, наконец введут псевод-тип для объединения array и ArrayAccess


    1. shandy
      13.09.2016 17:20

      А что в PHP есть встроенные классы, которые реализуют только интерфейс ArrayAccess?
      Насколько я помню (но могу и ошибаться), все кто реализует ArrayAccess так или иначе используют и Traversable интерфейс в конечном итоге. Например, тот же ArrayObject — он будет принят через «iterable» тип.
      Другой вопрос самописные классы — но в них нет проблем добавить еще один дополнительный интерфейс Iterator или IteratorAggregate и все так же будет работать…


      1. gro
        13.09.2016 17:47
        +2

        Но «возможность итерировать» и «хранилище с доступом по ключу», это разные интерфейсы.


        1. shandy
          13.09.2016 18:07

          Верно! Тогда почему от хранилища с доступом по ключу ждут возможность итерации (iterable)? )
          foreach для ArrayAccess никогда работал — https://3v4l.org/8H8vR


          1. gro
            14.09.2016 12:12

            Совершенно верно, никогда не работал и никто не ждёт.

            Есть два интерфейса — перебор элементов и доступ к конкретному элементу по ключу. Они не взаимозаменяемые.

            Нативный array «реализует» их оба.
            И для итерирования, наконец, его слили с объектами с нужным интерфейсом.
            А для доступа по ключу, опять забыли.


            1. shandy
              14.09.2016 15:15

              Вот теперь понял.
              Да действительно, для примитивного типа array и ArrayAccess придется создавать свой псевдотип (например «hashable»?) и оно никак не будет пересекается с «iterable».
              Пойти что-ли предложить им «hashable» RFC? ))


    1. SerafimArts
      14.09.2016 04:00
      +1

      Тогда в php 9.2 можно ожидать наконец введения типов, как в некоторых других языках? =)


      type likeArray = array|ArrayAccess;
      
      function a(likeArray $a) { ... }


  1. Dreyk
    13.09.2016 16:44

    ну вот зачем отдавать преимущество {} перед [] для доступа к строке? ничего оно путать не будет, в других языках ведь как-то не путаются. а вот разные скобки задолбаешься ставить.


    UPD: А не, они еще не решили


    Скрытый текст

    On the opposite side, it was also suggested that array access and string offsets are so closely-related concepts that we should recommend using '[]' in both cases and disable the alternate '{}' syntax for string offsets !


    So, as the subject is controversial and very tangential to the subject of this RFC, it will be left for a future RFC.


  1. artyfarty
    13.09.2016 16:54
    +2

    Изменение работы isset() в 7.0.6 резко охладило мой пыл обновляться. То что разработчики могут без лишнего шума в минорной версии могут решить всё сломать не греет душу. Я в целом за то чтобы убирать грязь из движка, но вот так вот, в минорных версиях, без настроек, без этапа депрекации – это опасно. Я ж не могу за весь composer.json отвечать.


    1. shandy
      13.09.2016 17:12

      Ну проколы бывают у всех. Это хотели сломать еще в 7.0.0 — что было бы естественным.

      Обьяснение
      It is unfortunate that this change had to happen in a patch release, rather than in PHP 7.0.0. It's my fault that I did not notice during the pre-release cycle that this previously relatively harmless bug completely breaks the null-coalesce operator in PHP 7. However, that's how things went, and at this point a fix was critical.

      For reference, the way to fix any issues with this change is to make sure your __isset() or offsetExists() functions behave correctly. If this change causes issues with your code, it indicates that you already have a broken __isset/offsetExists implementation, but it's brokenness did not previously surface (one bug canceling another...)


    1. baltazorbest
      13.09.2016 21:38
      +4

      А можете пожалуйста сказать, как именно изменили работу?


      1. artyfarty
        14.09.2016 13:19

        На хабре был пост: https://habrahabr.ru/post/283302/

        Из него не совсем ясны последствия, но хотя бы понятно что у себя проверять.


  1. JohnDaniels
    13.09.2016 16:55
    +1

    и что рекомендуется использовать вместо mcrypt?


    1. shandy
      13.09.2016 16:57

  1. syouth
    13.09.2016 16:58
    -2

    Кстати, это не значит что $x = someNethod(); не вернет ничего. Как и прежде в $x будет значение NULL

    У авторов php шмаль явно лучше чем у меня.


    1. AndreyRubankov
      13.09.2016 17:22
      +1

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

      Хотя с другой стороны, было бы не плохо кидать какой-то warning для такого кода, чтобы фиксили активнее.


      1. 4orever
        13.09.2016 21:06
        +2

        Простите, какая обратная совместимость? Речь о функции с возвращаемым типом void, который только-только вводится. В старых проектах таких ситуаций в принципе быть не может. Логично выкидывать предупреждение, если кто-то пытается присвоить значение функции с типом void.


        1. AntonShevchuk
          13.09.2016 22:41
          +1

          Может быть, если решили обновить зависимости, которые используются в текущем «старом» коде.


          1. 4orever
            14.09.2016 00:23

            Можете пояснить?


            1. Punk_UnDeaD
              14.09.2016 01:42

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


        1. Pakos
          14.09.2016 09:33

          PHP-программисты пишут всё с нуля и переписывают проект только целиком? Если использовать библиотеку, которая обновится и функция станет возвращать void, то это как раз тот случай.


          1. Source
            14.09.2016 11:18

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


            1. Pakos
              14.09.2016 12:49

              Так оно возвращало NULL и возвращает NULL, как понял.


              1. SerafimArts
                14.09.2016 14:22

                Именно так. Смысл void только в том, чтобы помечать методы (функции) у которых де-факто отсутствует возвращаемое значение. Можно было конечно не изобретать новый псевдотип, а использовать null:


                function a(): null {}

                Но с другой стороны такая конструкция позволяет писать:


                function a(): ?int {
                    return random_int(0, 1) ?: null;
                }
                
                function b(): null {
                  return a();
                }

                Что говорит о том, что ошибка может возникнуть в 50% случаях. А на void можно ругаться ещё на этапе парсинга тела метода\функции (скорее всего так и будет, я пока не пробовал войд на практике, не в курсе). Там не должно быть ничего кроме return; или же он (ретурн) просто должен отсутствовать, что избавляет от подобных проблем.


              1. Source
                14.09.2016 21:20

                Зачем записывать в переменную значение возвращаемое функцией, если оно всегда было NULL? А если оно не всегда возвращало NULL, а теперь возвращает void, то логика поменялась, а вызывающий код получил мину замедленного действия в виде отсутствия явной ошибки.


                1. VolCh
                  15.09.2016 23:59

                  Функции могут динамически подставляться


                  1. Source
                    16.09.2016 13:45

                    С разными типами возврата? Ну, право слово, PHP не обязательно использовать для извращений.


                    1. AndreyRubankov
                      16.09.2016 14:33

                      Фреймворк роутинга, который маппит HttpMethod + PATH [+MimeType] на функцию обработки.
                      Функция возвращает Объект, этот объект потом превращается в контент (к примеру в json или xml).

                      Функция может вернуть int, string, number, Object, null.

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


                      1. Source
                        16.09.2016 23:24

                        И? Вы не считаете такую архитектуру извращением?
                        Пока весь остальной мир идёт по пути унификации обработки веб-запросов (см. WSGI, Rack, Clack, Plug, WAI, etc.), Вы предлагаете из роутера передавать управление к разнородным функциям, которые хз что возвращают?
                        Ну, ok… ?\_(?)_/?


                        1. AndreyRubankov
                          17.09.2016 01:49

                          Посмотрел на WSGI и Rack (остальных не нашел), выглядит действительно не плохо! Но, как мне показалось, это то как на пхп писали еще лет 10 назад. Обработчик запроса, который устанавливает код ответа, хедеры и контент, и все это в одном месте. Для этого в пхп и фреймворки не нужны.

                          Описанный мною подход — это крайне упрощенный JAX-RS.
                          На выходе получается крайне простой и самодокументированный код, который не требует пояснений, который не содержит смеси бизнес-логики и формирования конента.

                          ps: вот пример из мира PHP, чтобы не было притензий, что я с монстроузорной java лезу тут: https://laravel.com/docs/5.3/routing


  1. zolt85
    13.09.2016 16:58

    Молодцы ребята, работают. Спасибо и за это!


  1. sumanai
    13.09.2016 17:36
    -2

    В наследнике можно делать «строже» возвращаемый тип (то есть запрещать nullable), а параметр наоборот расширять до nullable, НО не наоборот!

    Вот как вот с этим работать?


    1. AndreyRubankov
      13.09.2016 18:02

      Если работать через интерфейс, то более строгая имплементация ничего не поменяет. При этом, более строгая имплементация, сама по себе, запрещает коду сделать что-то не так.

      Эта фича в простом коде не будет сильно использоваться, но дает гибкость для построения абстракции (при разработке фреймворков и библиотек).


      1. sumanai
        14.09.2016 05:20

        Да я про то, что в одном месте можно расширять до nullable, в другом нельзя. И всё это нужно запоминать, логики лично я в этом не вижу.


        1. AndreyRubankov
          14.09.2016 08:56
          +1

          Да, это кажется странным на первый взгляд, но на самом деле все довольно просто.

          Интерфейс метода описывает максимальное допустимое множество значений, которые может обработать.
          Имплементация может расширить это множество, скажем обрабатывать не только int, но еще и long, — это не сломает интерфейс int это подмножество множества long. А вот вместо int принимать short мы уже не сможем, т.к. множество short меньше множества int.
          — а если работать через интерфейс, то сам интерфейс не позволит передать long, даже если в имплементации аргумент типа long.

          Примерно такой же подход при возврате значения, мы можем Уменьшать множество ответов, но не можем его расширять. Уменьшать мы можем только, если меньшее множество ответов является подмножеством большего.
          К примеру long в интерфейсе мы сможем заменить на int в имплементации, но вот заменить на bool — не сможем, bool — не является подмножеством множества long. (под bool подразумевается множество {true, false}, а не {1,0})

          ps: примеры long, int, bool приведены как ограниченные множества, чтобы проще было понять идею, в реальности такие манипуляции могут не пройти.


          1. sumanai
            14.09.2016 21:32

            Спасибо, теперь понятно, легко запомнить!


  1. Andrey_Volk
    13.09.2016 18:08
    +1

    У меня тут такой вопрос назрел: почему нельзя было сделать конструкцию

    void Test() { ... }

    как во многих языках программирования это реализовано. Зачем они сделали:
    function someNethod(): void { ... }

    ?
    Уж извините за такой вопрос, но он мне покоя не дает.


    1. MaximChistov
      13.09.2016 18:47

      Подозреваю что это нужно чтобы не сломать старый код в которым такие функции(без типа результата) возвращают значение.


    1. shandy
      13.09.2016 18:56
      +3

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


      1. 0x9d8e
        13.09.2016 20:17
        +2

        Эх. А ведь частенько руки сами набирают что-то типа protected int getNumber() {}. Вообще не очень понятно, почему бы от function не отказаться. Парисить чуть сложнее разве что.


    1. Dremkin
      13.09.2016 19:09

      К примеру, чтобы легко было найти поиском «function Test» в десятке мегабайт кода.


      1. ubuntu6610
        13.09.2016 23:01
        +1

        Легко нахожу «function Test» в десятке мегабайт кода с помощью IDE.


    1. xalkin
      13.09.2016 20:20
      -2

      Потому что это PHP, он не такой как все.


      1. SerafimArts
        14.09.2016 01:31

        1) Js + FlowType
        2) TypeScript
        3) Haxe
        4) ActionScript
        5) ...


        Ну да, уникальный в своём роде в этом случае.


      1. calliko
        14.09.2016 11:50
        -1

        Поэтому его все «любят»


    1. rdifb0
      14.09.2016 11:50
      +1

      Потому что обратная совместимость.
      Потому что родитель был Perl, а не Си.
      Потому что типизация пока по желанию.


    1. OnYourLips
      14.09.2016 11:59
      +1

      Потому что такой вариант синтаксиса не менее популярен. Более того, он гораздо удобнее читается (при чтении слева направо).
      И между «сделать, как в C» и «сделать удобно» выбрали второй вариант, и я их поддерживаю.


  1. Rathil
    14.09.2016 00:27

    Запрет на $this вне класса — сломается 1й Yii, там он в шаблонах использовался. Значит будем на 7.0.Х пробовать переезжать.


    1. sergebezborodov
      14.09.2016 07:42

      та он и во втором тоже используется


    1. AlexBond
      14.09.2016 11:49
      +2

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


    1. annenkov
      14.09.2016 15:44
      +1

      с чего взяли, что шаблоны YII1 вне класса работают? они инклудятся внутри метода