Vulpo One

PHP 7.0 yield

Однажды я жаловался, что

<?php
foreach((function () {for ($i = 0; $i < 10; $i++) yield $i;})() as $i) echo $i;

в PHP не работает. Так вот. Начиная с PHP7 благодаря новому парсингу синтаксиса

<?php
foreach((function () {for ($i = 0; $i < 10; $i++) yield $i;})() as $i) echo $i;  // 0123456789

очень даже работает.

Comments


PHP Inline Generator

Внезапно до меня дошло как записать эту вещь в 1 строчку

<?php
foreach(call_user_func(function () {for ($i = 0; $i < 10; $i++) yield $i;}) as $i) echo $i;

Т.е. имеем такой синтаксис для “одноразовых” генераторов

<?php
$generator = call_user_func(function () {
     while(1) {
        yield generate_something();
    }
});

Comments




PHP 5.6 — variadic func / splat operator

<?php

function func($a, $b, ...$params)
{
   var_dump($a, $b, $params);
}

func(1,2,3,4,5);
// int(1)
// int(2)
// array(3) {
//   [0]=>
//   int(3)
//  [1]=>
//  int(4)
//  [2]=>
//  int(5)
// }
// cool!

$a = [1,2,3,4,5];

func(...$a);
// int(1)
// int(2)
// array(3) {
//   [0]=>
//   int(3)
//  [1]=>
//  int(4)
//  [2]=>
//  int(5)
// }
// even cooler!

$a['qq'] = 'qq';

func(...$a);
// PHP Catchable fatal error:
// Cannot unpack array with string keys in /tmp/test.php on line 16
// okay :(

$b = [];
$b[1] = 1;
$b[4] = 4;
$b[2] = 2;
$b[5] = 5;
$b[3] = 3;

func(...$b);
// int(1)
// int(4)
// array(3) {
//   [0]=>
//   int(2)
//   [1]=>
//   int(5)
//   [2]=>
//   int(3)
// }
// WAT?

Comments


Libravatar Replace

Написал свою версию плагина для Libravatar — Libravatar Replace. Точнее, пришлось форкнуть оригинал, т.к. автор упорно молчал на мои попытки связаться. Сначала я добавил все дефолты Либраватара в список, а потом понеслось

Фичи:

  • Плагин полностью заменяет Gravatar на Libravatar в WordPress, включая всё default images (лого только меняется соответствующе)
  • Services_Libravatar содержит мой патч, исправляющий работу с портами в SRV
  • Попытался сделать поддержку BuddyPress (там свой алгоритм генерации изображний-аватаров, причем писец какой запутанный) — вроде работает

Comments


Про утечку ресурсов в генераторах PHP

Очень интересный и полезный момент сегодня всплыл в комментариях на Хабре. В PHP 5.5, как известно, сделали поддержку функций-генераторов, по типу питоновских. Там раньше были итераторы, но с адовым синтаксисом (как всё в SPL), а теперь ввели оператор ‘yield’ и всё волшебным образом упростилось.

Например, можно написать такой генератор, читающий построчно файл:

<?php
function getLines($file) {
    f = fopen($file, 'r');
    while ($line = fgets($f)) {
        yield $line;
    }
    fclose($f);
}

yield’ означает «вернуть значение и продолжить с этого места при следующем вызове функции». Имея такой генератор, можно сделать вот такую печать файла:

<?php
foreach (getLines("file.txt") as $line) {
      echo $line;
}

Удобно? Очень удобно. Оператор ‘yield’ выдаст все строки файла, а потом, когда файл закончится, произойдёт обычный ‘return’ из функции, который закроет генератор (и закончит цикл).

Но как известно, если всё идёт хорошо, значит, вы чего-то не заметили. Немного изменим наш цикл:

<?php
foreach (getLines("file.txt") as $n => $line) {
    if ($n > 5) break;
    echo $line;
}

Предположим, нас интересуют только первые шесть строк файла, а дальше мы хотим прервать цикл оператором ‘break’. Имеем на то полное право. Но что в этом случае произойдёт внутри генератора? А ничего. Он останется стоять на последнем исполненном yield-е и никогда не дойдёт до строки ‘fclose($f)’. И наш файл останется незакрытым.

Мы получили утечку ресурса (открытого файла). Понятно, что внутри генератора могут быть открыты любые ресурсы и объекты, и их необходимо правильно и предсказуемо закрывать. Но как это сделать, если юзер может в любой момент сделать break? Обычная документация (http://www.php.net/manual/en/language.generators.overview.php) никаких намёков не даёт.

Так вот, оказывается (и за это спасибо юзеру weirdan с Хабра: https://habr.com/ru/post/189796/#comment_6594776), что читать в этом случае надо не документацию, а RFC по генераторам: https://wiki.php.net/rfc/generators#closing_a_generator. А в нём сказано, что при освобождении ссылки на генератор, у него внутри обязаны выполниться все блоки ‘finally’. И тогда мы получаем очень простой, красивый и безопасный код:

<?php
function getLines($file) {
    f = fopen($file, 'r');
    try {
        while ($line = fgets($f)) {
            yield $line;
        }
    } finally {
        fclose($f);
    }
}

В этом случае блок ‘finally’ выполнится и при нормальном выходе из цикла по генератору и при выходе по break-у. Ура.

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

ru-php.livejournal.com/1566325.html

Comments



PHP 5.5 yield

Извращаюсь с новой игрушкой

<?php
$a = function () {for ($i = 0; $i < 10; $i++) yield $i;}; foreach ($a() as $i) echo $i; // 0123456789

Жаль, что не работает foreach ($a as $i) echo $i;, тогда можно было бы написать короче

<?php
foreach(function () {for ($i = 0; $i < 10; $i++) yield $i;} as $i) echo $i;

Или будь PHP похож на одного из своих предков, можно было бы

<?php
foreach((function () {for ($i = 0; $i < 10; $i++) yield $i;})() as $i) echo $i;

Даже извращения не работают типа

<?php
foreach((function () {for ($i = 0; $i < 10; $i++) yield $i;})->__invoke() as $i) echo $i;

Если кто не догнал, я пытаюсь сделать вот это:

(0..9).each { |i| print i }

Comments


Adding Java Style

Оппа, оппа, Java Style

Что делает эта программа?

 1<?php
 2
 3use SimplePHPEasyPlus\Number\NumberCollection;
 4use SimplePHPEasyPlus\Number\SimpleNumber;
 5use SimplePHPEasyPlus\Number\CollectionItemNumberProxy;
 6use SimplePHPEasyPlus\Parser\SimpleNumberStringParser;
 7use SimplePHPEasyPlus\Iterator\CallbackIterator;
 8use SimplePHPEasyPlus\Operator\AdditionOperator;
 9use SimplePHPEasyPlus\Operation\ArithmeticOperation;
10use SimplePHPEasyPlus\Operation\OperationStream;
11use SimplePHPEasyPlus\Engine;
12use SimplePHPEasyPlus\Calcul\Calcul;
13use SimplePHPEasyPlus\Calcul\CalculRunner;
14
15
16$numberCollection = new NumberCollection();
17
18$numberParser = new SimpleNumberStringParser();
19
20$firstParsedNumber = $numberParser->parse('1');
21$firstNumber = new SimpleNumber($firstParsedNumber);
22$firstNumberProxy = new CollectionItemNumberProxy($firstNumber);
23
24$numberCollection->add($firstNumberProxy);
25
26$secondParsedNumber = $numberParser->parse('1');
27$secondNumber = new SimpleNumber($secondParsedNumber);
28$secondNumberProxy = new CollectionItemNumberProxy($secondNumber);
29
30$numberCollection->add($secondNumberProxy);
31
32$addition = new AdditionOperator('SimplePHPEasyPlus\Number\SimpleNumber');
33
34$operation = new ArithmeticOperation($addition);
35
36$engine = new Engine($operation);
37
38$calcul = new Calcul($engine, $numberCollection);
39
40$runner = new CalculRunner();
41
42$runner->run($calcul);
43
44$result = $calcul->getResult();
45$numericResult = $result->getValue(); // 2

Она вычисляет 1+1!!!

https://habr.com/ru/post/172119/

Comments