PHP-Дайджест № 140 (17 – 30 сентября 2018)


    Свежая подборка со ссылками на новости и материалы. В выпуске: PHP 7.3 RC2, Codeception 2.5 и другие релизы, типизированные свойства в PHP 7.4, концепт предзагрузки кода от Дмитрия Стогова, порция полезных инструментов, и многое другое.
    Приятного чтения!



    Новости и релизы



    PHP Internals


    • [RFC] Typed Properties 2.0 — Предложение по типизированным свойства принято практически единогласно: 70 против 1! Типизированные свойства будут добавлены в PHP 7.4.
      Скрытый текст
      class Example {
          // All types with the exception of "void" and "callable" are supported
          public int $scalarType;
          protected Type $type;
          private ?Type $nullableType;
       
          // Types are also legal on static properties
          public static iterable $staticProp;
       
          // Types can also be used with the "var" notation
          var bool $flag;
       
          // Typed properties may have default values (more below)
          public string $str = "foo";
          public ?string $nullableStr = null;
       
          // The type applies to all properties in one declaration
          public float $x, $y;
          // equivalent to:
          public float $x;
          public float $y;
      }
      
    • [PR] An attempt to implement «preloading» ability — Дмитрий Стогов главный автор проекта PHPNG, который стал базой для PHP 7. На этот раз Дмитрий представил концепт, который позволяет приложениям предварительно загружать код еще до того как PHP обработает HTTP-запрос.
      Также Дмитрий работает над реализацией JIT в PHP, которая ожидается в PHP 8.


    Инструменты


    • spiral/roadrunner — Высокопроизводительный сервер приложений, балансировщик нагрузки и менеджер процессов для PHP реализованный на Go. Добавлена поддержка HTTPS и HTTP/2 Push.
    • ankitpokhrel/tus-php — Реализация сервера и клиента tus.io открытого протокола возобновляемых загрузок файлов. Протокол используется в Vimeo, Cloudflare, и Git LFS.
    • TiBeN/CrontabManager — Менеджер cron-задач.
    • paragonie/easydb — Простая и удобная обертка над PDO. Доступно кэширование подготовленных запросов paragonie/easydb-cache.
    • zendframework/zend-diactoros 2.0.0 — Обновление популярной реализации PSR-7 и теперь еще PSR-17.
    • liuggio/fastest — Простое решение для параллельного запуска тестов. Альтернатива более сложному paratestphp/paratest
    • konsulting/porter — Мультисайт окружение для локальной разработки на базе Docker.


    Материалы для обучения




    Аудио и видеоматериалы




    Занимательное



    Спасибо за внимание!

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

    Прислать ссылку
    Поиск ссылок по всем дайджестам
    Предыдущий выпуск: PHP-Дайджест № 139

    Zfort Group

    303,90

    Компания

    Поделиться публикацией

    Похожие публикации

    Комментарии 10
      0
      pronskiy ошибочка «Пошаговое УРКоводство по созданию первого приложения на Laravel»
        +7
        Зато звучит
          0
          Поправил, спасибо
          0
          Говорили, говорили, говорили не размещать .svn, .git, .env в папках, которые доступны веб-серверу, а воз и нынче там…
            0
            Где-то на StackOverflow видел как рекомендовали при использовании Apache все с public выносить в корень, так как он дефолтно смотрит в корень xDDD
            +1
            [RFC] Typed Properties 2.0 — Предложение по типизированным свойства принято практически единогласно: 70 против 1! Типизированные свойства будут добавлены в PHP 7.4.

            А против проголосовал как раз Дмитрий Стогов. Интересно, кстати, почему?
              +2
              Думаю из-за оверхеда по производительности gist.github.com/dstogov/b9fc0fdccfb8bf7bae121ce3d3ff1db1 ведь Дмитрий как раз производительностью занимается, а эта фича ухудшает ее.
                +3
                О, да, вполне возможно, кстати. Но по-моему, что это всего лишь обуславливается «сыростью» патча, т.к. он на перспективу позволяет облегчить Zval, избавив поля от лишних данных в режиме strict_types.
                +3
                Ник на рэддите [объяснял](https://www.reddit.com/r/PHP/comments/9j2oel/rfc_about_typed_properties_has_been_accepted/e6o94hd/).
                0

                Прикольный слон. Кстати сделан скорее всего с помощью https://github.com/fogleman/primitive.


                Вот бота писал для телеграмм


                Заголовок спойлера
                const fs = require('fs');
                const exec = require('child_process').execSync;
                const temp = require("temp").track();
                const pathComponent = require('path');
                const TelegramBot = require('node-telegram-bot-api');
                
                const Settings = function () {
                    this.defaultSettings = {
                        rect: 1,
                        type: 0
                    };
                
                    this.container = {};
                };
                
                Settings.prototype = {
                    getValue: function (chatId, value = null) {
                        if (this.container.hasOwnProperty(chatId)) {
                            return value ? this.container[chatId][value] : this.container[chatId]
                        }
                
                        return value ? this.getDefault()[value] : this.getDefault();
                    },
                    setValue: function (chatId, key, value) {
                        if (!this.container.hasOwnProperty(chatId)) {
                            this.container[chatId] = this.getDefault();
                        }
                
                        if (Object.keys(this.defaultSettings).indexOf(key) === -1) {
                            console.log('Try to set unknown setting: ' + key);
                            return;
                        }
                
                        if (key === 'type' || key === 'rect') {
                            value = parseInt(value, 10)
                        }
                
                        this.container[chatId][key] = value;
                    },
                    getDefault: function () {
                        return Object.assign({}, this.defaultSettings);
                    }
                };
                
                const settingsContainer = new Settings();
                
                // Устанавливаем токен, который выдавал нам бот.
                const token = '';
                const downloadDir = './downloaded/';
                
                const rectOptions = {
                    reply_markup: JSON.stringify({
                        inline_keyboard: [
                            [{text: 'Треугольники', callback_data: 'rect_1'}],
                            [{text: 'Прямоугольники', callback_data: 'rect_2'}],
                            [{text: 'Эллипсы', callback_data: 'rect_3'}],
                            [{text: 'Круги', callback_data: 'rect_4'}],
                            [{text: 'Прямоугольники(rotated)', callback_data: 'rect_5'}],
                            [{text: 'Кривые', callback_data: 'rect_6'}],
                            [{text: 'Эллипсы(rotated)', callback_data: 'rect_7'}],
                            [{text: 'Полигоны', callback_data: 'rect_8'}],
                        ]
                    })
                };
                
                const typeOptions = {
                    reply_markup: JSON.stringify({
                        inline_keyboard: [
                            [{text: 'Простая картинка', callback_data: 'type_0'}],
                            [{text: 'Движущаяся картинка', callback_data: 'type_1'}]
                        ]
                    })
                };
                
                // Включить опрос сервера
                const bot = new TelegramBot(token, {polling: true});
                
                bot.onText(/\/start/, function (msg) {
                    bot.sendMessage(msg.chat.id, '/status /settype /setfig');
                });
                
                bot.onText(/\/status/, function (msg) {
                    bot.sendMessage(msg.chat.id, 'I am alive!');
                });
                
                bot.onText(/\/setfig/, function (msg) {
                    bot.sendMessage(msg.chat.id, 'Выберите фигуры из которых будет формируется изображение:', rectOptions);
                });
                
                bot.onText(/\/settype/, function (msg) {
                    bot.sendMessage(msg.chat.id, 'Выберите тип возвращаемого изображения:', typeOptions);
                });
                
                bot.on('callback_query', function (msg) {
                    let chatId = msg.message.chat.id;
                
                    let answer = msg.data.split('_');
                    if (answer.length === 2) {
                        settingsContainer.setValue(chatId, answer[0], answer[1]);
                    }
                    bot.sendMessage(chatId, 'ok');
                });
                
                bot.on('message', function (msg) {
                   console.log(msg); 
                });
                
                bot.on('photo', onPhoto);
                
                async function onPhoto (msg) {
                    let chatId = msg.chat.id;
                
                    try {
                        let image = initImage(msg);
                        let originalImagePath = await bot.downloadFile(image.file_id, downloadDir);
                        let ext = pathComponent.extname(originalImagePath);
                        let resizedImagePath = temp.path({suffix: ext});
                        let processedImagePath;
                
                        await resizeImage(image, originalImagePath, resizedImagePath);
                
                        let settings = settingsContainer.getValue(chatId);
                        console.log(settings);
                        if (settings.type === 0) {
                            processedImagePath = temp.path({suffix: ext});
                            makeSimpleImage(settings, resizedImagePath, processedImagePath);
                            await bot.sendPhoto(chatId, processedImagePath, {});
                        } else if (settings.type === 1) {
                            processedImagePath = temp.path({suffix: '.gif'});
                            await makeGif(settings, image, resizedImagePath, processedImagePath);
                            await bot.sendVideo(chatId, processedImagePath, {});
                        }
                
                        temp.cleanupSync();
                    } catch (err) {
                        console.log(err);
                        return bot.sendMessage(chatId, err);
                    }
                }
                
                function initImage(msg) {
                    let photo = msg.photo || [];
                    let lastFileSize = 0;
                    let maxResId = -1;
                    let i;
                
                    for (i = 0; i < photo.length; i++) {
                        if ((photo[i].file_size || 0) > lastFileSize) {
                            maxResId = i;
                        }
                    }
                
                    if (maxResId === -1) {
                        throw new Error('No photo info');
                    }
                
                    let msgPhoto = photo[maxResId];
                    ['file_id', 'width', 'height'].forEach(function (prop) {
                        if (!msgPhoto.hasOwnProperty(prop)) {
                            throw new Error('No photo size');
                        }
                    });
                
                    return msgPhoto;
                }
                
                function resizeImage (image, from_path, to_path) {
                    let resize = image.width > image.height ? '256x' : 'x256';
                    return exec('convert -geometry ' + resize + ' ' + from_path + ' ' + to_path)
                }
                
                function makeSimpleImage(settings, from_path, to_path) {
                    return exec('primitive -n 250 -m ' + settings.rect + ' -i ' + from_path + ' -o ' + to_path)
                }
                
                async function makeGif(settings, image, from_path, to_path) {
                    let dir = './series/' + makeRandName();
                    let size = image.width > image.height ? '512:' : ':512';
                
                    if (!fs.existsSync(dir)){
                        fs.mkdirSync(dir);
                    }
                
                    for (let i = 1; i <=5 ; i++) {
                        makeSimpleImage(settings, from_path, '/ ' + i + '.svg');
                        exec('node node_modules/svgexport/bin/index.js ' + dir + '/' + i + '.svg ' + dir + '/' + i +'.png ' + size);
                    }
                
                    let makeGifCommand = 'convert -loop 0 -delay 12 ' +
                        dir + '/1.png ' +
                        dir + '/2.png ' +
                        dir + '/3.png ' +
                        dir + '/4.png ' +
                        dir + '/5.png ' +
                        to_path;
                
                    console.log(makeGifCommand);
                
                    return exec(makeGifCommand);
                }
                
                function makeRandName() {
                    let text = "";
                    let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
                
                    for (let i = 0; i < 8; i++)
                        text += possible.charAt(Math.floor(Math.random() * possible.length));
                
                    return text;
                }

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

                Самое читаемое