PHP-Дайджест № 114 – свежие новости, материалы и инструменты (1 – 14 августа 2017) |
Свежая подборка со ссылками на новости и материалы. В выпуске: PHP 7.2.0 Beta 2, об истории и безопасности unserialize(), старт PSR HTTP Client, StackOverflow Driven Development, видео с конференций и митапов, и многое другое.
Приятного чтения!
PSA: Don't use unserialize() on untrusted input (see https://t.co/8GZb1xqE1u)
— Nikita Popov (@nikita_ppv) August 10, 2017
PHP will no longer treat unserialize() bugs are security bugs.
StackOverflowBuddy::substringBetweenTwoStrings('platypus', 'pl', 'us');
// atyp
Спасибо за внимание!
Если вы заметили ошибку или неточность — сообщите, пожалуйста, в личку.
Вопросы и предложения пишите на почту или в твиттер.
Прислать ссылку
Быстрый поиск по всем дайджестам
<- Предыдущий выпуск: PHP-Дайджест № 113
Метки: author pronskiy разработка веб-сайтов php дайджест php- ссылки symfony yii laravel zend reactphp psr |
[Из песочницы] Расчет приоритета комбинаций в техасском холдеме (покере) на PHP |
function cardsCreation()
{
$arrayCards = [];
for ($i = 0; $i < 7; $i++) {
$card = mt_rand(2, 14); // создаю случайное число в диапозоне от 2 до 14
$multiplier = mt_rand(0, 3); //формирую случайным образом множитель
if (1 == $multiplier) { //если множитель равен 1, добавляю к числу 100
$card = $card + 100;
} else if (2 == $multiplier) { //если множитель равен 2, добавляю к числу 200
$card = $card + 200;
} else if (3 == $multiplier) { //если множитель равен 3, добавляю к числу 300
$card = $card + 300;
}
if (!in_array($card, $arrayCards)) { //проверяю, есть ли в массиве такое число, если нет добавляю его в массив
$arrayCards = array_merge($arrayCards, [$card]);
} else { // если число есть в массиве, откатываю цикл, чтобы сформировать новое число
$i--;
}
}
return $arrayCards;
}
function straight(array $arrayCards) {
$newArrayCards = [];
$ace = false;
foreach ($arrayCards as $arrayCard) {
$newArrayCards = array_merge($newArrayCards, [$arrayCard % 100]); //создаю массив с остатками от деления на 100
if(14 == $arrayCard % 100) { //проверка на туз для комбинации А, 2, 3, 4, 5
$ace = true;
}
}
if($ace == true) {
$newArrayCards = array_merge($newArrayCards, [1]); //если в массиве присутствует туз, добавляю к массиву 1
}
rsort($newArrayCards); //сортирую массив с числами в порядке убывания
$count = 0; //счетчик, к которому добавляется 1 в случае, если разница между текущим элементом цикла и предыдущим = 1
$length = 4; //число, показывает до какого элемента массива проверять
$result = 0; //окончательный результат
$begin = 0; //число показывает, с какого элемента массива проверять
for($i = 1; $i <= $length; $i++) {
if (-1 == ($newArrayCards[$i] - $newArrayCards[$i - 1])) {
$count++;
}
if($length == $i) {
if(4 == $count) { //$count == 4, когда стрит
$result = $newArrayCards[$begin] * 1e+8;
}
else if($length < 6 or (6 == $length and $ace == true)) {//если стрита нет, идем еще на один круг
$length++; //увеличиваем число, до которого будем проверять
$begin++; //увеличиваем число, с которого будем проверять
$i = $begin; //при попадании в for к $i автоматически добавится 1 ($i++ в for)
$count = 0; //обнуляю счетчик
}
}
}
return $result;
}
function pokerFlush(array $arrayCards) {
$suit1 = []; //первая масть
$suit2 = []; //вторая масть
$suit3 = []; //третья масть
$suit4 = []; //четвертая масть
foreach ($arrayCards as $arrayCard) { //создаю 4 массива, содержащих разные масти исходного массива
if($arrayCard >= 2 and $arrayCard <= 14) {
$suit1 = array_merge($suit1, [$arrayCard]);
} else if($arrayCard >= 102 and $arrayCard <= 114) {
$suit2 = array_merge($suit2, [$arrayCard]);
} else if($arrayCard >= 202 and $arrayCard <= 214) {
$suit3 = array_merge($suit3, [$arrayCard]);
} else {
$suit4 = array_merge($suit4, [$arrayCard]);
}}
if(count($suit1) >= 5) { //если количество карт первой масти больше или равно 5
$result = straightFlush($suit1); //проверяю не образует ли данная комбинация стрит флеш
if(0 == $result) {//если стрит флеша нет
foreach ($suit1 as $key1 => $s1) {//выбираю остатки от деления на 100
$suit1[$key1] = $s1 % 100;
}
rsort($suit1); //сортирую массив по убыванию
$result = $suit1[0] * 1e+10 + $suit1[1] * 1e+8 + $suit1[2] * 1e+6 + $suit1[3] * 1e+4 + $suit1[4] * 1e+2;
}
} else if (count($suit2) >= 5) { //если количество карт второй масти больше или равно 5
$result = straightFlush($suit2);
if(0 == $result) {
foreach ($suit2 as $key2 => $s2) {
$suit2[$key2] = $s2 % 100;
}
rsort($suit2);
$result = $suit2[0] * 1e+10 + $suit2[1] * 1e+8 + $suit2[2] * 1e+6 + $suit2[3] * 1e+4 + $suit2[4] * 1e+2;
}
} else if (count($suit3) >= 5) { //если количество карт третьей масти больше или равно 5
$result = straightFlush($suit3);
if(0 == $result) {
foreach ($suit3 as $key3 => $s3) {
$suit3[$key3] = $s3 % 100;
}
rsort($suit3);
$result = $suit3[0] * 1e+10 + $suit3[1] * 1e+8 + $suit3[2] * 1e+6 + $suit3[3] * 1e+4 + $suit3[4] * 1e+2;
}
} else if (count($suit4) >= 5) { //если количество карт четвертой масти больше или равно 5
$result = straightFlush($suit4);
if(0 == $result) {
foreach ($suit4 as $key4 => $s4) {
$suit4[$key4] = $s4 % 100;
}
rsort($suit4);
$result = $suit4[0] * 1e+10 + $suit4[1] * 1e+8 + $suit4[2] * 1e+6 + $suit4[3] * 1e+4 + $suit4[4] * 1e+2;
}
} else {
$result = 0;
}
return $result;
}
function straightFlush(array $arrayCards) {
$newArrayCards = [];
$ace = false;
foreach ($arrayCards as $arrayCard) {
$newArrayCards = array_merge($newArrayCards, [$arrayCard % 100]);
if (14 == $arrayCard % 100) {
$ace = true;
}
}
if ($ace == true) {
$newArrayCards = array_merge($newArrayCards, [1]);
}
rsort($newArrayCards);
$count = 0;
$length = 4;
$result = 0;
$begin = 0;
for ($i = 1; $i <= $length; $i++) {
if (-1 == ($newArrayCards[$i] - $newArrayCards[$i - 1])) {
$count++;
}
if ($length == $i) {
if (4 == $count) {
$result = $newArrayCards[$begin] * 1e+16;
} else if ((7 == count($arrayCards) and ($length < 6 or (6 == $length and $ace == true))) or
(6 == count($arrayCards) and ($length < 5 or (5 == $length and $ace == true))) or //если число элементов исходного массива = 6
(5 == count($arrayCards) and (5 == $length and $ace == true))) { //если число элементов исходного массива = 5
$length++;
$begin++;
$i = $begin;
$count = 0;
}
}
}
return $result;
}
function couple(array $arrayCards) {
$newArrayCards = [];
foreach ($arrayCards as $arrayCard) {
$newArrayCards = array_merge($newArrayCards, [$arrayCard % 100]); //создаю массив с остатками от деления на 100
}
rsort($newArrayCards); //сортирую массив с числами в порядке убывания
$count1 = 0; //счетчик для первой пары
$count2 = 0; //счетчик для второй пары
$count3 = 0; //счетчик для третьей пары
$match1 = 0; //сюда положу значение первой пары
$match2 = 0; //сюда положу значение второй пары
$match3 = 0; //сюда положу значение третьей пары
for($i = 1; $i < count($newArrayCards); $i++) {
if ($newArrayCards[$i] == $match1 or $match1 == 0) { //первое парное сочетание
if ($newArrayCards[$i] == $newArrayCards[$i - 1]) {
$match1 = $newArrayCards[$i];
$count1++;
}
} else if ($newArrayCards[$i] == $match2 or $match2 == 0) { //второе парное сочетание
if ($newArrayCards[$i] == $newArrayCards[$i - 1]) {
$match2 = $newArrayCards[$i];
$count2++;
}
} else if ($newArrayCards[$i] == $match3 or $match3 == 0) { //третье парное сочетание
if ($newArrayCards[$i] == $newArrayCards[$i - 1]) {
$match3 = $newArrayCards[$i];
$count3++;
}
}
}
//здесь я преобразую 111 к 110 (2 пары) и 211 к 210 (фулл хаус)
if(($count1 == 1 or $count1 == 2) and $count2 == 1 and $count3 == 1) {
$count3 = 0;
}
//здесь я преобразую 121 сначала к 211 для простоты вычислений, а затем к 210 (фулл хаус)
else if($count2 == 2 and $count1 == 1 and $count3 == 1) {
$support = $match2;
$match2 = $match1;
$match1 = $support;
$count1 = 2;
$count2 = 1;
$count3 = 0;
}
//здесь я преобразую 112 сначала к 211 для простоты вычислений, а затем к 210 (фулл хаус)
else if($count3 == 2 and $count1 == 1 and $count2 == 1) {
$support = $match3;
$match2 = $match1;
$match1 = $support;
$count1 = 2;
$count3 = 0;
}
//здесь я преобразую 220 к 210 (фулл хаус)
else if($count1 == 2 and $count2 == 2 and $count3 == 0) {
$count2 = 1;
}
//здесь я преобразую 120 к 210 (фулл хаус)
else if ($count1 == 1 and $count2 == 2 and $count3 == 0) {
$support = $match1;
$match1 = $match2;
$match2 = $support;
$count1 = 2;
$count2 = 1;
}
//320 к 300 и 310 к 300
else if($count1 == 3 and ($count2 == 2 or $count2 == 1)) {
$count2 = 0;
}
//230 к 320 и затем к 300 и 130 к 310 и затем к 300
else if($count2 == 3 and($count1 == 2 or $count1 == 1)) {
$support = $match2;
$match2 = $match1;
$match1 = $support;
$count1 = 3;
$count2 = 0;
}
//каре
if ($count1 == 3) {
$count1 = 1e+14;
}
//фулл хаус
else if ($count1 == 2 and $count2 == 1) {
$count1 = 1e+12;
$count2 = 1e+10;
}
//тройка
else if ($count1 == 2 and $count2 == 0) {
$count1 = 1e+6;
}
//2 пары
else if ($count1 == 1 and $count2 == 1) {
$count1 = 1e+4;
$count2 = 1e+2;
}
//пара
else if ($count1 == 1 and $count2 == 0) {
$count1 = 1e+2;
}
$result = $match1 * $count1 + $match2 * $count2;// $match1 и $match2 будут равны 0, если совпадений не было
return $result;
}
function priority(array $arrayCards) {
//здесь я определяю старшую карту
if($arrayCards[5] % 100 > $arrayCards[6] % 100) { //условились, что две последние карты массива - карманные
$highCard1 = $arrayCards[5] % 100;
$highCard2 = $arrayCards[6] % 100;
} else {
$highCard1 = $arrayCards[6] % 100;
$highCard2 = $arrayCards[5] % 100;
}
$flush = pokerFlush($arrayCards); //вызываю функцию для расчета флеша
$straight = straight($arrayCards); //вызываю функцию для расчета стрита
$couple = couple($arrayCards); //вызываю функцию для расчета пар
//далее определяю результат согласно приоритету комбинаций
if($flush >= 1e+16) {
$result = $flush; //стрит флеш
} else if($couple >= 1e+14 and $couple < 1e+16) {
$result = $couple; //каре
} else if($couple >= 1e+12 and $couple < 1e+14) {
$result = $couple; //фулл хаус
} else if($flush >= 1e+10) {
$result = $flush; //флеш
} else if($straight >= 1e+8 and $straight < 1e+10) {
$result = $straight; //стрит
} else if($couple >= 1e+6 and $couple < 1e+8) {
$result = $couple; //тройка
} else if($couple >= 1e+4 and $couple < 1e+6) {
$result = $couple; //две пары
} else if($couple >= 1e+2 and $couple < 1e+4) {
$result = $couple; //пара
} else {
$result = $highCard1 + $highCard2 * 1e-2; //старшая карта
}
return $result;
}
100/12 * ($arrayCards[$i] % 100 - 2)
100/4 * floor($arrayCards[$i] / 100)
Метки: author heartsease php poker texas holdem |
TSP problem. Mixed algorithm |
Метки: author Grossmend алгоритмы |
Итак, вы решили развернуть OpenStack |
Метки: author divanikus системное администрирование облачные вычисления виртуализация openstack облака |
5 популярных мифов о дизайне логотипов |
Метки: author Logomachine брендинг блог компании логомашина дизайн логотип миф видео |
Дайджест интересных материалов для мобильного разработчика #216 (6 августа — 13 августа) |
![]() |
Сначала они воруют, а когда ты побеждаешь, то тебя убивают |
![]() |
Заблуждения Clean Architecture |
![]() |
Разработка интерфейса приложения для пожизненного использования на примере мобильного дневника диабета |
|
Optimization Unity3d UI by GPU (for example minimap) или создаем миникарту без дополнительных камер и спрайтов |
streeter12 4 июля 2016 в 14:51
Данный метод несмотря на свою простоту имеет явные недостатки.
1. Низкая производительность.
2. Для добавления новых меток надо создавать новые сферы (лишний хлам в префабах).
3. Добавление новых типов меток и их фильтров для различных игроков сильно затруднено.
4. Для смены внешнего вида метки необходимо создавать меш!
5. Лишние объекты на каждой сцене => лишняя сложность => сложнее разработка и поддержка.
6. Сложно тестировать => больше возможных багов (с учетом 3).
static const int MaxCount = 256;
float4 _Targets[MaxCount];
float _WorldSizeX;
float _WorldSizeY;
protected virtual void Update ()
{
for (int i = 0; i < list.Length; i++)
{
list[i] = new Vector4(0, 0, 0, -1);
}
for (int i = 0; i < list.Length && i < targets.Count ; i++)
{
list[i] = new Vector4(targets[i].x *k.x, targets[i].y, targets[i].z*k.y, targets[i].w);
}
image.GetModifiedMaterial(image.material).SetVectorArray("_Targets", list);
}
fixed a = _Targets[i].y;
fixed resultX = tX;
fixed resultY = tY;
if (a != 0)
{
fixed x0 = minX + (sizeX * 0.5);
fixed y0 = minY + (sizeY * 0.5);
resultX = x0 + (tX - x0) * cos(a) - (tY - y0) * sin(a);
resultY = y0 + (tY - y0) * cos(a) + (tX - x0) * sin(a);
}
protected virtual void Start ()
{
tests = new List();
float x = 10;
float y = 10;
for (int i = 0; i < 256; i++)
{
int idx = Random.Range(0, prefabs.Count);
GameObject obj = Instantiate(prefabs[idx].gameObject);
RectTransform t = obj.GetComponent();
t.SetParent(parent);
t.localPosition = new Vector3(startPosition.x + x, startPosition.y + y, 0);
if (useRotation)
{
t.rotation = Quaternion.Euler(0, 0, Random.Range(0, 360));
}
x += step;
if (i % 20 == 0)
{
y += step;
x = 0;
}
tests.Add(t);
}
}
Заполнения через прокси класс для шейдера
item.type = Random.Range(0, 64); означает тип иконки
void Start () {
tests = new List();
float x = 10;
float z = 10;
for (int i = 0; i < 256; i++)
{
Item2 item = new Item2();
item.position = new Vector3(x, 0, z);
if (useRotation)
{
item.rotation = Random.Range(0, 360);
}
item.type = Random.Range(0, 64);
x += step;
if (i % 20 == 0)
{
z += step;
x = 0;
}
tests.Add(item);
}
}
Профайлер для шейдера (на сцене еще 3 куба)

Профайлер для спрайтов (на сцене еще 3 image в качестве префабов)

и напоследок пример с маской.

P.S.
"Если можешь что-то посчитать на GPU, делай это"
Метки: author derek_streyt разработка игр клиентская оптимизация unity3d c# optimization gpu shader ui |
[Из песочницы] Получение записей телефонной книги с мобильного телефона без дисплея |
Метки: author yaanyk восстановление данных телефонная книга |
[Из песочницы] Безопасный OpenVPN на VPS за несколько минут |
Метки: author xl-tech системное администрирование сетевые технологии *nix vpn openvpn vps блокировки информационная безопасность скрипт bash- |
Использование ImGui с SFML для создания инструментов для разработки игр |
Привет, Хабр!
Данная статья — вольный перевод моей статьи на русский с некоторыми небольшими изменениями и улучшениями. Хотелось бы показать как просто и полезно использовать ImGui с SFML. Приступим.
При разработке игр очень важно иметь хорошие инструменты для создания контента (редактор уровней, ресурсов и пр.), а также для дебаггинга. Наличие данных вещей повышает производительность и креативность. Гораздо проще отлавливаются баги и исправляются уже найденные: легко выводить значение различных переменных, а также создавать виджеты для их изменения, чтобы посмотреть, что происходит с игрой при определённых их значениях. Виджеты для изменения переменных также очень полезны для полировки геймплея. Например, можно легко изменять скорость передвижения персонажей, скорость перезарядки оружия и пр.
Вот какие инструменты я создал с помощью ImGui для своей игры:
Редактор уровней
Консоль Lua
Редактор анимаций
Как можно видеть, в ImGui есть достаточно разных виджетов, чтобы создавать полезные и удобные интерфейсы.
Immediate mode GUI немного отличается от классической методики программирования интерфейсов, которая называется retained mode GUI. ImGui виджеты создаются и рисуются в каждом кадре игрового цикла. Сами виджеты не хранят внутри себя своё состояние, либо хранят абсолютно минимальный необходимый минимум, который обычно скрыт от программиста.
В отличие от того же Qt, где для создания кнопки нужно создавать объект QPushButton
, а затем связывать с ней какую-нибудь функцию-callback, вызываемую при нажатии, в ImGui всё делается гораздо проще. В коде достаточно написать:
if (ImGui::Button("Some Button")) {
... // код, вызываемый при нажатии кнопки
}
Данный код должен вызываться в каждой итерации игрового цикла, в которой эта кнопка должна быть доступна пользователю.
Изначально данный концепт может показаться странным и очень неэффективным, однако это всё работает так быстро в сравнении с остальным кодом, что в результате даже сложные интерфейсы не привносят сильных изменений в производительность игры.
Советую посмотреть вот это видео Кейси Муратори про ImGui, если вы хотите узнать чуть больше о данной методике.
Итак, каковы же достоинства ImGui?
Итак, начнём
Добавьте следующие файлы в билд вашего проекта:
Вот небольшой пример кода, который создаёт окошко ImGui и позволяет менять цвет заднего фона и заголовок окна. Объяснения того, что происходит, будут далее.
#include "imgui.h"
#include "imgui-sfml.h"
#include Graphics/RenderWindow.hpp>
#include System/Clock.hpp>
#include Window/Event.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(640, 480), "");
window.setVerticalSyncEnabled(true);
ImGui::SFML::Init(window);
sf::Color bgColor;
float color[3] = { 0.f, 0.f, 0.f };
// здесь мы будем использовать массив char. Чтобы использовать
// std::string нужно сделать действия, описанные во второй части
char windowTitle[255] = "ImGui + SFML = <3";
window.setTitle(windowTitle);
sf::Clock deltaClock;
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
ImGui::SFML::ProcessEvent(event);
if (event.type == sf::Event::Closed) {
window.close();
}
}
ImGui::SFML::Update(window, deltaClock.restart());
ImGui::Begin("Sample window"); // создаём окно
// Инструмент выбора цвета
if (ImGui::ColorEdit3("Background color", color)) {
// код вызывается при изменении значения, поэтому всё
// обновляется автоматически
bgColor.r = static_cast(color[0] * 255.f);
bgColor.g = static_cast(color[1] * 255.f);
bgColor.b = static_cast(color[2] * 255.f);
}
ImGui::InputText("Window title", windowTitle, 255);
if (ImGui::Button("Update window title")) {
// этот код выполняется, когда юзер жмёт на кнопку
// здесь можно было бы написать
// if(ImGui::InputText(...))
window.setTitle(windowTitle);
}
ImGui::End(); // end window
window.clear(bgColor); // заполняем окно заданным цветом
ImGui::SFML::Render(target);
window.display();
}
ImGui::SFML::Shutdown();
}
Вы должны увидеть что-то вроде этого:
Попробуйте изменить что-нибудь. Если кликнуть два раза на одно из полей RGB, то можно ввести соответствующее значение. Если одно из полей потянуть, то можно плавно изменять текущее введённое значение. Поле ввода позволяет изменить заголовок окна после нажатия на кнопку.
Отлично, теперь разберёмся как всё работает.
ImGui инициализируется вызовом ImGui::SFML::Init
, при вызове в функцию передаётся ссылка на окно sf::RenderWindow
. В этот момент также создаётся стандартный шрифт, который будет использоваться в дальнейшем. (см. раздел Fonts how-to в описании imgui-sfml, чтобы увидеть как использовать другие шрифты).
При выходе из программы важно вызывать функцию ImGui::SFML::Shutdown
, которая освобождает ресурсы, которые использует ImGui.
В игровом цикле ImGui имеет две фазы: обновление и рендеринг.
Обновление состоит из обработки событий, обновления состояния ImGui и обновления/создания виджетов. Обработка событий происходит через вызов ImGui::SFML::ProcessEvent
. ImGui обрабатывает события клавиатуры, мыши, изменения фокуса и размера окна. Обновление состояния ImGui производится в ImGui::SFML::Update
, в неё передаётся delta time (время между двумя обновлениями), который ImGui использует для обновления состояния виджетов (например, для анимации). Также в данной функции вызывается ImGui::NewFrame
, после вызова которой уже можно создавать новые виджеты.
Рендеринг ImGui осуществляется вызовом ImGui::SFML::Render
. Очень важно создавать/обновлять виджеты между вызовами ImGui::SFML::Update
и ImGui::SFML::Render
, иначе ImGui будет ругаться на нарушение состояния.
Если вы рендерите реже, чем обновляете ввод и игру, то в конце каждой итерации вашего update
необходимо также вызывать ImGui::EndFrame
:
while (gameIsRunning) {
while (updateIsNeeded()) {
updateGame(dt);
ImGui::SFML::Update(window, dt);
ImGui::EndFrame();
}
renderGame();
}
Виджеты создаются путём вызова соответствующих функций (например, ImGui::InputInt
или ImGui::Button
). Если вызвать ImGui::ShowTestWindow
, то можно увидеть много примеров использования ImGui, весь код можно найти в imgui_demo.cpp.
Для SFML в биндинге были созданы некоторые перегрузки функций, например в ImGui::Image
и ImGui::ImageButton
можно кидать sf::Sprite
и sf::Texture
, также можно легко рисовать линии и прямоугольники вызовом DrawLine
, DrawRect
и DrawRectFilled
.
Вот такая библиотека: проста в использовании и настройке, и очень полезна для создания инструментов и дебагинга. Приятного использования!
P.S. Если возникнет интерес, то могу перевести и вторую часть туториала, которая рассказывает про использование ImGui с современным C++ и стандартной библиотекой. Советую обратить на статью внимание тем, кто решит использовать (или уже использует) ImGui: она показывает как просто решать основные проблемы ImGui и делать всё проще и безопаснее, чем это делается в C++03.
Метки: author eliasdaler разработка игр программирование c++ sfml gamedev gamedevelopment gui imgui tutorial |
Проклятые Земли — Улучшаем бег и опыт с напарниками |
.text:00548315 loc_548315:
.text:00548315 fld dword ptr [edi+14h]
.text:00548318 fld dword ptr [edi+18h]
.text:0054831B fmul ds:dbl_73F088
.text:00548321 fsubp st(1), st
.text:00548323 fst dword ptr [edi+14h]
.text:00548326 > fcomp ds:flt_73B858
.text:0054832C fnstsw ax
.text:0054832E test ah, 1
.text:00548331 jz short loc_548388
fst dword ptr [edi+14h]
. Можно поставить Operand type — Floating point на этот адрес и на соседний [edi+18h]
.[edi+14h]
— текущее значение[edi+18h]
— максимальное значениеdbl_73F088
, и результат вычитается из текущего значения. Поэтому все персонажи бегают одинаково..rdata:0073F088 dbl_73F088 dq 6.666666666666666e-3
; 6.666666666666666e-3 = 0.006666666666666666 = 1/150
4E 1B E8 B4 81 4E 7B 3F
0.0066666666666666667 - 0x3F7B4E81B4E81B4F
0.006666666666666666 - 0x3F7B4E81B4E81B4E
0.0022222222222222222 - 0x3F623456789ABCDF
0.002222222222222222 - 0x3F623456789ABCDE
0x3F623456789ABCDF
DF BC 9A 78 56 34 62 3F
rep movsd
..text:00522D00 fld dword ptr [ebx+700h]
.text:00522D06 fadd dword ptr [ebx+4]
.text:00522D09 fsub dword ptr [ebx+8]
.text:00522D0C fstp [ebp+var_10]
.text:00522D0F fld [ebp+var_10]
.text:00522D12 fistp [ebp+var_C]
.text:00522D15 mov edx, [ebp+var_C]
.text:00522D18 mov [edi+8], edx
.text:00522D1B > mov eax, [ebx+10h]
.text:00522D1E mov [ebp+var_10], eax
ebx
находится адрес объекта персонажа. В [ebx+700h]
находится 0.[ebx+4]
..text:005239D7 fld ds:dbl_73E128
.text:005239DD fld dword ptr [esi+20h]
.text:005239E0 fsub ds:flt_73E124
.text:005239E6 call __CIpow
.text:005239EB fmul [ebp+arg_4]
.text:005239EE fadd dword ptr [esi+700h]
.text:005239F4 fcom ds:flt_73B858
.text:005239FA fst dword ptr [esi+700h]
.text:00523A00 fnstsw ax
.text:00523A02 test ah, 41h
.text:00523A05 jnz short loc_523A19
.text:00523A07 fadd dword ptr [esi+4]
.text:00523A0A mov dword ptr [esi+700h], 0
.text:00523A14 fstp dword ptr [esi+4]
.text:00523A17 > jmp short loc_523A1B
[esi+4]
новое значение опыта. В [ebp+arg_4]
число 2.0. За молодого кабана дают 4.0, значит деление находится до вызова функции..text:00591521 loc_591521:
.text:00591521 fild [ebp+var_18]
.text:00591524 xor esi, esi
.text:00591526 cmp eax, edi
.text:00591528 mov [ebp+var_14], esi
.text:0059152B fdivr [ebp+arg_4]
.text:0059152E fstp [ebp+arg_4]
.text:00591531 jle short loc_5915A5
.text:00591533 jmp short loc_591537
.text:00591324 fdiv [ebp+var_18]
// было
.00591521: DB45E8 fild d,[ebp][-018]
.00591524: 33F6 xor esi,esi
.00591526: 3BC7 cmp eax,edi
.00591528: 8975EC mov [ebp][-014],esi
.0059152B: D87D0C fdivr d,[ebp][00C]
.0059152E: D95D0C fstp d,[ebp][00C]
.00591531: 7E72 jle .0005915A5
.00591533: EB02 jmps .000591537
// стало
.00591521: 909090 nop
.00591524: 33F6 xor esi,esi
.00591526: 3BC7 cmp eax,edi
.00591528: 8975EC mov [ebp][-014],esi
.0059152B: 909090 nop
.0059152E: 909090 nop
.00591531: 7E72 jle .0005915A5
.00591533: EB02 jmps .000591537
Метки: author michael_vostrikov реверс-инжиниринг разработка игр evil islands проклятые земли бег персонажа опыт напарников |
Проклятые Земли — Улучшаем бег и опыт с напарниками |
.text:00548315 loc_548315:
.text:00548315 fld dword ptr [edi+14h]
.text:00548318 fld dword ptr [edi+18h]
.text:0054831B fmul ds:dbl_73F088
.text:00548321 fsubp st(1), st
.text:00548323 fst dword ptr [edi+14h]
.text:00548326 > fcomp ds:flt_73B858
.text:0054832C fnstsw ax
.text:0054832E test ah, 1
.text:00548331 jz short loc_548388
fst dword ptr [edi+14h]
. Можно поставить Operand type — Floating point на этот адрес и на соседний [edi+18h]
.[edi+14h]
— текущее значение[edi+18h]
— максимальное значениеdbl_73F088
, и результат вычитается из текущего значения. Поэтому все персонажи бегают одинаково..rdata:0073F088 dbl_73F088 dq 6.666666666666666e-3
; 6.666666666666666e-3 = 0.006666666666666666 = 1/150
4E 1B E8 B4 81 4E 7B 3F
0.0066666666666666667 - 0x3F7B4E81B4E81B4F
0.006666666666666666 - 0x3F7B4E81B4E81B4E
0.0022222222222222222 - 0x3F623456789ABCDF
0.002222222222222222 - 0x3F623456789ABCDE
0x3F623456789ABCDF
DF BC 9A 78 56 34 62 3F
rep movsd
..text:00522D00 fld dword ptr [ebx+700h]
.text:00522D06 fadd dword ptr [ebx+4]
.text:00522D09 fsub dword ptr [ebx+8]
.text:00522D0C fstp [ebp+var_10]
.text:00522D0F fld [ebp+var_10]
.text:00522D12 fistp [ebp+var_C]
.text:00522D15 mov edx, [ebp+var_C]
.text:00522D18 mov [edi+8], edx
.text:00522D1B > mov eax, [ebx+10h]
.text:00522D1E mov [ebp+var_10], eax
ebx
находится адрес объекта персонажа. В [ebx+700h]
находится 0.[ebx+4]
..text:005239D7 fld ds:dbl_73E128
.text:005239DD fld dword ptr [esi+20h]
.text:005239E0 fsub ds:flt_73E124
.text:005239E6 call __CIpow
.text:005239EB fmul [ebp+arg_4]
.text:005239EE fadd dword ptr [esi+700h]
.text:005239F4 fcom ds:flt_73B858
.text:005239FA fst dword ptr [esi+700h]
.text:00523A00 fnstsw ax
.text:00523A02 test ah, 41h
.text:00523A05 jnz short loc_523A19
.text:00523A07 fadd dword ptr [esi+4]
.text:00523A0A mov dword ptr [esi+700h], 0
.text:00523A14 fstp dword ptr [esi+4]
.text:00523A17 > jmp short loc_523A1B
[esi+4]
новое значение опыта. В [ebp+arg_4]
число 2.0. За молодого кабана дают 4.0, значит деление находится до вызова функции..text:00591521 loc_591521:
.text:00591521 fild [ebp+var_18]
.text:00591524 xor esi, esi
.text:00591526 cmp eax, edi
.text:00591528 mov [ebp+var_14], esi
.text:0059152B fdivr [ebp+arg_4]
.text:0059152E fstp [ebp+arg_4]
.text:00591531 jle short loc_5915A5
.text:00591533 jmp short loc_591537
.text:00591324 fdiv [ebp+var_18]
// было
.00591521: DB45E8 fild d,[ebp][-018]
.00591524: 33F6 xor esi,esi
.00591526: 3BC7 cmp eax,edi
.00591528: 8975EC mov [ebp][-014],esi
.0059152B: D87D0C fdivr d,[ebp][00C]
.0059152E: D95D0C fstp d,[ebp][00C]
.00591531: 7E72 jle .0005915A5
.00591533: EB02 jmps .000591537
// стало
.00591521: 909090 nop
.00591524: 33F6 xor esi,esi
.00591526: 3BC7 cmp eax,edi
.00591528: 8975EC mov [ebp][-014],esi
.0059152B: 909090 nop
.0059152E: 909090 nop
.00591531: 7E72 jle .0005915A5
.00591533: EB02 jmps .000591537
Метки: author michael_vostrikov реверс-инжиниринг разработка игр evil islands проклятые земли бег персонажа опыт напарников |
[Перевод] Переосмысление PID 1. Часть 2 |
Метки: author houk системное администрирование серверное администрирование настройка linux *nix systemd linux sysvinit |
[Перевод] Переосмысление PID 1. Часть 2 |
Метки: author houk системное администрирование серверное администрирование настройка linux *nix systemd linux sysvinit |
Информационная экономика: почему стоимость технологических компаний так высока |
Метки: author itinvest финансы в it блог компании itinvest финансы ит биржа капитализация инвестиции |
[Из песочницы] Разработка telegram бота с использованием Spring |
Пишите телеграмм ботов? Ваша производительность разработки желает лучшего? Ищите чего-то нового? Тогда прошу под кат.
Идея заключается в следующем: слямзить архитектуру spring mvc и перенести на telegram api.
Выглядеть должно как-то так:
@BotController
public class SimpleOkayController {
@BotRequestMapping(value = "/ok")
public SendMessage ok(Update update) {
return new SendMessage()
.setChatId(update.getMessage().getChatId())
.setText("okay bro, okay!");
}
}
или
@BotController
public class StartController {
@Autowired
private Filter shopMenu;
@Autowired
private PayTokenService payTokenService;
@Autowired
private ItemService itemService;
@BotRequestMapping("/shop")
public SendMessage generateInitMenu(Update update) {
return new SendMessage()
.setChatId(update.getMessage().getChatId().toString())
.setText("Товары моего магазинчика!")
.setReplyMarkup(shopMenu.getSubMenu(0L, 4L, 1L)); // <--
}
@BotRequestMapping(value = "/buyItem", method = BotRequestMethod.EDIT)
public List bayItem(Update update) {
....................
Item item = itemService.findById(id); // <--
return Arrays.asList(new EditMessageText()
.setChatId(update.getMessage().getChatId())
.setMessageId(update.getMessage().getMessageId())
.setText("Подтвердите ваш выбор, в форме ниже"),
new SendInvoice()
.setChatId(Integer.parseInt(update.getMessage().getChatId().toString()))
.setDescription(item.getDescription())
.setTitle(item.getName())
.setProviderToken(payTokenService.getPayToken())
........................
.setPrices(item.getPrice())
);
}
}
Это даёт следующие преимущества:
Давайте теперь посмотрим как это можно завести в нашем проекте
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface BotController {
String[] value() default {};
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface BotRequestMapping {
String[] value() default {};
BotRequestMethod[] method() default {BotRequestMethod.MSG};
}
Создаем свой контейнер обработчиков в виде обычной HashMap
public class BotApiMethodContainer {
private static final Logger LOGGER = Logger.getLogger(BotApiMethodContainer.class);
private Map controllerMap;
public static BotApiMethodContainer getInstanse() {
return Holder.INST;
}
public void addBotController(String path, BotApiMethodController controller) {
if(controllerMap.containsKey(path)) throw new BotApiMethodContainerException("path " + path + " already add");
LOGGER.trace("add telegram bot controller for path: " + path);
controllerMap.put(path, controller);
}
public BotApiMethodController getBotApiMethodController(String path) {
return controllerMap.get(path);
}
private BotApiMethodContainer() {
controllerMap = new HashMap<>();
}
private static class Holder{
final static BotApiMethodContainer INST = new BotApiMethodContainer();
}
}
В контейнере будем хранить контроллеры обертки (для пары @BotController и @BotRequestMapping)
public abstract class BotApiMethodController {
private static final Logger LOGGER = Logger.getLogger(BotApiMethodController.class);
private Object bean;
private Method method;
private Process processUpdate;
public BotApiMethodController(Object bean, Method method) {
this.bean = bean;
this.method = method;
processUpdate = typeListReturnDetect() ? this::processList : this::processSingle;
}
public abstract boolean successUpdatePredicate(Update update);
public List process(Update update) {
if(!successUpdatePredicate(update)) return null;
try {
return processUpdate.accept(update);
} catch (IllegalAccessException | InvocationTargetException e) {
LOGGER.error("bad invoke method", e);
}
return null;
}
boolean typeListReturnDetect() {
return List.class.equals(method.getReturnType());
}
private List processSingle(Update update) throws InvocationTargetException, IllegalAccessException {
BotApiMethod botApiMethod = (BotApiMethod) method.invoke(bean, update);
return botApiMethod != null ? Collections.singletonList(botApiMethod) : new ArrayList<>(0);
}
private List processList(Update update) throws InvocationTargetException, IllegalAccessException {
List botApiMethods = (List) method.invoke(bean, update);
return botApiMethods != null ? botApiMethods : new ArrayList<>(0);
}
private interface Process{
List accept(Update update) throws InvocationTargetException, IllegalAccessException;
}
}
Теперь когда у нас есть данная кодовая база возникает вопрос: как Spring заставить автоматически наполнять контейнер, чтобы мы могли им пользоваться?
Для этого реализуем специальный бин — BeanPostProcessor. Это дает возможность отлавливать бины во время их инициализации. Наши контроллеры имеют scope по умолчанию — синглтон, значит инициализироваться они будут со стартом контекста!
@Component
public class TelegramUpdateHandlerBeanPostProcessor implements BeanPostProcessor, Ordered {
private static final Logger LOGGER = Logger.getLogger(TelegramUpdateHandlerBeanPostProcessor.class);
private BotApiMethodContainer container = BotApiMethodContainer.getInstanse();
private Map botControllerMap = new HashMap<>();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Class
Метки: author PqDn java spring telegram bots |
[Из песочницы] Пишем расширения для PHP 7 на C++ |
ext_skel
. Она позволяет создать болванку для нового расширения. Мы будем править код, который получился после выполнения этой команды./ext
исходных кодов PHP и от туда выполнить ext_skel
с указанием имени нового расширения:./ext_skel --extname=phpcv
tests
и файлы config.m4
, php_phpcv.h
и phpcv.c
. Файл phpcv.c
сразу переименуем в phpcv.cpp
.phpize
для подготовки нашего расширения к компилированию.acinclude.m4
и aclocal.m4
в исходном коде php, и написаны на языке из скобочек и знаков препинания. На самом деле достаточно почитать коменты, которые начинаются со строк «dnl» и будет более или менее понятно, что эти макросы делают.
PHP_ARG_ENABLE(phpcv, whether to enable phpcv support,
[ --enable-phpcv Enable phpcv support])
if test "$PHP_PHPCV" != "no"; then
PHP_REQUIRE_CXX()
SEARCH_PATH="/usr/local /usr /opt/local"
SEARCH_FOR="/include/opencv2/opencv.hpp"
if test -r $PHP_PHPCV/$SEARCH_FOR; then
CV_DIR=$PHP_PHPCV
else
AC_MSG_CHECKING([for opencv in default path])
for i in $SEARCH_PATH ; do
if test -r $i/$SEARCH_FOR; then
CV_DIR=$i
AC_MSG_RESULT(found in $i)
break
fi
done
fi
if test -z "$CV_DIR"; then
AC_MSG_RESULT([not found])
AC_MSG_ERROR([Please reinstall the OpenCV distribution])
fi
AC_CHECK_HEADER([$CV_DIR/include/opencv2/objdetect/objdetect.hpp], [], AC_MSG_ERROR('opencv2/objdetect/objdetect.hpp' header not found))
AC_CHECK_HEADER([$CV_DIR/include/opencv2/highgui/highgui.hpp], [], AC_MSG_ERROR('opencv2/highgui/highgui.hpp' header not found))
PHP_ADD_LIBRARY_WITH_PATH(opencv_objdetect, $CV_DIR/lib, PHPCV_SHARED_LIBADD)
PHP_ADD_LIBRARY_WITH_PATH(opencv_highgui, $CV_DIR/lib, PHPCV_SHARED_LIBADD)
PHP_ADD_LIBRARY_WITH_PATH(opencv_imgproc, $CV_DIR/lib, PHPCV_SHARED_LIBADD)
PHP_SUBST(PHPCV_SHARED_LIBADD)
PHP_NEW_EXTENSION(phpcv, phpcv.cpp, $ext_shared,, -std=c++0x -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
fi
#ifndef PHP_PHPCV_H
#define PHP_PHPCV_H
#define PHP_PHPCV_EXTNAME "phpcv"
#define PHP_PHPCV_VERSION "0.2.0"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
extern "C" {
#include "php.h"
#include "ext/standard/info.h"
}
#ifdef ZTS
#include "TSRM.h"
#endif
extern zend_module_entry phpcv_module_entry;
#define phpext_phpcv_ptr &phpcv_module_entry
#if defined(ZTS) && defined(COMPILE_DL_PHPCV)
ZEND_TSRMLS_CACHE_EXTERN();
#endif
#endif /* PHP_PHPCV_H */
extern "C" { ... }
/**
* @see cv::CascadeClassifier::detectMultiScale()
* @param string $imgPath
* @param string $cascadePath
* @param double $scaleFactor
* @param int $minNeighbors
*
* @return array
*/
function cv_detect_multiscale($imgPath, $cascadePath, $scaleFactor, $minNeighbors) {
}
#include "php_phpcv.h"
#include objdetect/objdetect.hpp>
#include highgui/highgui.hpp>
#include imgproc/imgproc.hpp>
PHP_MINFO_FUNCTION(phpcv) {
php_info_print_table_start();
php_info_print_table_header(2, "phpcv support", "enabled");
php_info_print_table_end();
}
PHP_FUNCTION(cv_detect_multiscale) {
char *imgPath = NULL, *cascadePath = NULL;
long imgPathLen, cascadePathLen, minNeighbors;
double scaleFactor, minWidth, minHeight;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssdl", &imgPath, &imgPathLen, &cascadePath, &cascadePathLen, &scaleFactor, &minNeighbors) == FAILURE) {
RETURN_FALSE;
}
// Read Image
cv::Mat image;
image = cv::imread(imgPath, CV_LOAD_IMAGE_GRAYSCALE);
if (image.empty()) {
RETURN_FALSE;
}
equalizeHist(image, image);
//min size for detected object, discarding objects smaller than this
minWidth = image.size().width / 10;
minHeight = image.size().height / 10;
// Load Face cascade (.xml file)
cv::CascadeClassifier faceCascade;
if (!faceCascade.load(cascadePath)) {
RETURN_FALSE;
}
// Detect faces
std::vector faces;
faceCascade.detectMultiScale(image, faces, scaleFactor, minNeighbors, 0, cv::Size(minWidth, minHeight));
array_init(return_value);
// Build array to return
for ( int i = 0; i < faces.size(); i++ ) {
// Now we have: faces[i].x faces[i].y faces[i].width faces[i].height
zval face;
array_init(&face);
add_assoc_long(&face, "x", faces[i].x);
add_assoc_long(&face, "y", faces[i].y);
add_assoc_long(&face, "w", faces[i].width);
add_assoc_long(&face, "h", faces[i].height);
add_next_index_zval(return_value, &face);
}
}
const zend_function_entry phpcv_functions[] = {
PHP_FE(cv_detect_multiscale, NULL)
PHP_FE_END
};
zend_module_entry phpcv_module_entry = {
STANDARD_MODULE_HEADER,
PHP_PHPCV_EXTNAME,
phpcv_functions,
NULL,
NULL,
NULL,
NULL,
PHP_MINFO(phpcv),
PHP_PHPCV_VERSION,
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_PHPCV
#ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE();
#endif
ZEND_GET_MODULE(phpcv)
#endif
zend_parse_parameters
идёт код С++. С помощью библиотеки opencv находим лица и формируем выходной массив с координатами найденных лиц.NULL
подряд — это вместо методов, которые выполняются при инициализации и shutdown расширения и запроса, нам просто нечего делать во время этих фаз.tests
--TEST--
Test face detection
--SKIPIF--
--FILE--
--EXPECT--
face detection works
phpize && ./configure && make
make test
PHP_ARG_ENABLE(intl-dtpg, whether to enable intl-dtpg support,
[ --enable-intl-dtpg Enable intl-dtpg support])
if test "$PHP_INTL_DTPG" != "no"; then
PHP_SETUP_ICU(INTL_DTPG_SHARED_LIBADD)
PHP_SUBST(INTL_DTPG_SHARED_LIBADD)
PHP_REQUIRE_CXX()
PHP_NEW_EXTENSION(intl_dtpg, intl_dtpg.cpp, $ext_shared,,-std=c++0x $ICU_INCS -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
fi
$ICU_INCS
— специфичные для ICU флаги.#ifndef INTL_DTPG_H
#define INTL_DTPG_H
#include
#include
#include dtptngen.h>
extern "C" {
#include "php.h"
#include "ext/standard/info.h"
}
extern zend_module_entry intl_dtpg_module_entry;
#define phpext_intl_dtpg_ptr &intl_dtpg_module_entry
#define INTL_DTPG_VERSION "1.0.0"
#ifdef ZTS
#include "TSRM.h"
#endif
typedef struct {
DateTimePatternGenerator *dtpg;
UErrorCode status;
zend_object zo;
} IntlDateTimePatternGenerator_object;
static inline IntlDateTimePatternGenerator_object *php_intl_datetimepatterngenerator_fetch_object(zend_object *obj) {
return (IntlDateTimePatternGenerator_object *)((char*)(obj) - XtOffsetOf(IntlDateTimePatternGenerator_object, zo));
}
#if defined(ZTS) && defined(COMPILE_DL_INTL_DTPG)
ZEND_TSRMLS_CACHE_EXTERN()
#endif
#endif /* INTL_DTPG_H */
IntlDateTimePatternGenerator_object
. В ней хранится указатель на объект DateTimePatternGenerator
из библиотеки ICU, переменная для хранения статуса и объект zend_object
, который представляет собой PHP класс. Это такая обёртка для C++ класса. API PHP оперирует объектом zend_object
, а мы завернули его в струкутуру и всегда имеем доступ к тому, что находится «по соседству» с zend_object
.inline
функции для извлечения структуры IntlDateTimePatternGenerator_object
имея объект zend_object
. Под капотом мы делаем в памяти шаг назад от начала zend_object
на размер нашей структуры минус размер zend_object
. Таким образом мы оказываемся как раз в начале структуры, указатель на которую нам и возвращается. Такой хитрый способ подсмотрен в исходных кодах расширения intl.class IntlDateTimePatternGenerator
{
/**
* @param string $locale
*/
public function __construct(string $locale) {}
/**
* Return the best pattern matching the input skeleton.
* It is guaranteed to have all of the fields in the skeleton.
*
* @param string $skeleton The skeleton is a pattern containing only the variable fields.
* For example, "MMMdd" and "mmhh" are skeletons.
* @return string The best pattern found from the given skeleton.
*/
public function findBestPattern(string $skeleton) {}
}
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "intl_dtpg.h"
#include ustdio.h>
#include smpdtfmt.h>
zend_class_entry *IntlDateTimePatternGenerator_ce;
zend_object_handlers IntlDateTimePatternGenerator_object_handlers;
/* {{{ IntlDateTimePatternGenerator_objects_dtor */
static void IntlDateTimePatternGenerator_object_dtor(zend_object *object)
{
zend_objects_destroy_object(object);
}
/* }}} */
/* {{{ IntlDateTimePatternGenerator_objects_free */
void IntlDateTimePatternGenerator_object_free(zend_object *object)
{
IntlDateTimePatternGenerator_object *dtpgo = php_intl_datetimepatterngenerator_fetch_object(object);
zend_object_std_dtor(&dtpgo->zo);
dtpgo->status = U_ZERO_ERROR;
if (dtpgo->dtpg) {
delete dtpgo->dtpg;
dtpgo->dtpg = nullptr;
}
}
/* }}} */
/* {{{ IntlDateTimePatternGenerator_object_create */
zend_object *IntlDateTimePatternGenerator_object_create(zend_class_entry *ce)
{
IntlDateTimePatternGenerator_object* intern;
intern = (IntlDateTimePatternGenerator_object*)ecalloc(1,
sizeof(IntlDateTimePatternGenerator_object) + zend_object_properties_size(ce));
zend_object_std_init(&intern->zo, ce);
object_properties_init(&intern->zo, ce);
intern->dtpg = nullptr;
intern->status = U_ZERO_ERROR;
intern->zo.handlers = &IntlDateTimePatternGenerator_object_handlers;
return &intern->zo;
}
/* }}} */
/* {{{ proto void IntlDateTimePatternGenerator::__construct(string $locale)
* IntlDateTimePatternGenerator object constructor.
*/
PHP_METHOD(IntlDateTimePatternGenerator, __construct)
{
zend_string *locale;
zval *object;
IntlDateTimePatternGenerator_object* dtpg = nullptr;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &locale) == FAILURE) {
return;
}
object = getThis();
dtpg = php_intl_datetimepatterngenerator_fetch_object(Z_OBJ_P(object));
dtpg->status = U_ZERO_ERROR;
dtpg->dtpg = DateTimePatternGenerator::createInstance(Locale(ZSTR_VAL(locale)), dtpg->status);
}
/* }}} */
/* {{{ proto string IntlDateTimePatternGenerator::findBestPattern(string $skeleton)
* Return the best pattern matching the input skeleton.
*/
PHP_METHOD(IntlDateTimePatternGenerator, findBestPattern)
{
zend_string *skeleton;
zval *object;
IntlDateTimePatternGenerator_object* dtpg = nullptr;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &skeleton) == FAILURE) {
return;
}
object = getThis();
dtpg = php_intl_datetimepatterngenerator_fetch_object(Z_OBJ_P(object));
UnicodeString pattern = dtpg->dtpg->getBestPattern(UnicodeString(ZSTR_VAL(skeleton)), dtpg->status);
std::string s;
pattern.toUTF8String(s);
RETURN_STRING(s.c_str());
}
/* }}} */
ZEND_BEGIN_ARG_INFO_EX(arginfo_findBestPattern, 0, 0, 1)
ZEND_ARG_INFO(0, skeleton)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, 0, 1)
ZEND_ARG_INFO(0, locale)
ZEND_END_ARG_INFO()
const zend_function_entry IntlDateTimePatternGenerator_functions[] = {
PHP_ME(IntlDateTimePatternGenerator, __construct, arginfo___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
PHP_ME(IntlDateTimePatternGenerator, findBestPattern, arginfo_findBestPattern, ZEND_ACC_PUBLIC)
PHP_FE_END
};
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(intl_dtpg)
{
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "IntlDateTimePatternGenerator", IntlDateTimePatternGenerator_functions);
ce.create_object = IntlDateTimePatternGenerator_object_create;
IntlDateTimePatternGenerator_ce = zend_register_internal_class(&ce);
memcpy(&IntlDateTimePatternGenerator_object_handlers, zend_get_std_object_handlers(),
sizeof IntlDateTimePatternGenerator_object_handlers);
IntlDateTimePatternGenerator_object_handlers.offset =
XtOffsetOf(IntlDateTimePatternGenerator_object, zo);
IntlDateTimePatternGenerator_object_handlers.clone_obj = NULL; //no clone support
IntlDateTimePatternGenerator_object_handlers.dtor_obj = IntlDateTimePatternGenerator_object_dtor;
IntlDateTimePatternGenerator_object_handlers.free_obj = IntlDateTimePatternGenerator_object_free;
if(!IntlDateTimePatternGenerator_ce) {
zend_error(E_ERROR, "Failed to register IntlDateTimePatternGenerator class");
return FAILURE;
}
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION(intl_dtpg)
{
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(intl_dtpg)
{
php_info_print_table_start();
php_info_print_table_header(2, "intl_dtpg support", "enabled");
php_info_print_table_header(2, "intl_dtpg version", INTL_DTPG_VERSION);
php_info_print_table_end();
}
/* }}} */
/* {{{ intl_dtpg_functions[]
*/
const zend_function_entry intl_dtpg_functions[] = {
PHP_FE_END
};
/* }}} */
/* {{{ intl_dtpg_module_entry
*/
zend_module_entry intl_dtpg_module_entry = {
STANDARD_MODULE_HEADER,
"intl_dtpg",
intl_dtpg_functions,
PHP_MINIT(intl_dtpg),
PHP_MSHUTDOWN(intl_dtpg),
NULL,
NULL,
PHP_MINFO(intl_dtpg),
INTL_DTPG_VERSION,
STANDARD_MODULE_PROPERTIES
};
/* }}} */
#ifdef COMPILE_DL_INTL_DTPG
#ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
#endif
ZEND_GET_MODULE(intl_dtpg)
#endif
zend_object
, сбрасываем статус и уничтожаем объект С++ класса.DateTimePatternGenerator
.zend_string
объекта, при этом в zend_parse_parameters указывается большая «S». Это нововведение в PHP 7. Но и старый способ с указанием маленькой «s» и получением отдельно C-style строки и её длины тоже работает, как мы видели в предыдущем расширении.zend_API.h
можно понять, что они значат.SUCCESS
, а можно и не указывать и ниже, в zend_module_entry, указать NULL
.NULL
это про request, мы ничего не делаем в момент инициализации и shutdown запроса.--TEST--
Check for intl_dtpg presence
--SKIPIF--
--FILE--
--EXPECT--
d MMMM;MMMM d
phpize && ./configure && make
make test
extension=modules/intl_dtpg.so
Метки: author xBazilio php c++ php-extension |
[Перевод] Практический бизнес онтологии: рассказ c передовой |
|
Как попасть в ТОП: PR ДО релиза и в случае провала |
Нет тонкой грани между «мне нравится» и «действительно нужно\красиво». Это довольно толстая линия и называется она – профессионализм.
|
[recovery mode] IOTV — простой HTTP протокол для работы с сообщениями и командами IOT объектов в сервисе VIALATM |
Метки: author Euler2012 разработка мобильных приложений разработка для интернета вещей блог компании euler2012.com iot ioe mobile development |